강의

멘토링

커뮤니티

인프런 커뮤니티 질문&답변

rhdrlehf5님의 프로필 이미지
rhdrlehf5

작성한 질문수

토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1

문서와 코드 다듬기

repository에 직접 접근

작성

·

72

0

repository에 직접 접근하는 기준이 어떤건지 궁금합니다.
강의에서 예를 들면

@Override
public Member updateInfo(Long memberId, MemberInfoUpdateRequest memberInfoUpdateRequest) {
    Member member = memberFinder.find(memberId);

    checkDuplicateProfile(member, memberInfoUpdateRequest.profileAddress());

    member.updateInfo(memberInfoUpdateRequest);

    return memberRepository.save(member);
}

private void checkDuplicateProfile(Member member, String profileAddress) {
    if(profileAddress.isEmpty()) return;
    Profile currentProfile = member.getDetail().getProfile();

    if(currentProfile != null && currentProfile.address().equals(profileAddress)) return;

    if(memberRepository.findByProfile(new Profile(profileAddress)).isPresent()){
        throw new DuplicateProfileException("이미 존재하는 프로필 주소입니다: " + profileAddress);
    }
}

이 부분에서 member를 찾아올 때는 memberFinder를 통해서 가져왔는데 아래 중복 체크에서는 repository를 바로 사용했습니다. 물론 강의에서 profile을 찾는거기에 memberFinder에 넣기에는 책임이 난잡해질 것 같다는 느낌은 듭니다만 여기서 구분한 기준이 있는지 궁금합니다.
다른 예를 들면

public LoginResponse login(LoginRequest request) {

        Member member = memberFinder.findByEmail(request.email());

        if(!member.verifyPassword(request.password(),  passwordEncoder)){
            //일치하지 않으면 에러
        }
       
        //이 부분은 직접 접근하는게 좋은지
        member.updateLastLoginAt();
        memberRepository.save(member);

        //아니면 memberUpdater라는 포트를 통해 접근하는게 좋은지
        memberUpdater.updateLastLoginAt();

        return new LoginResponse(member.getId(), member.getStatus());
    }

이런 경우 둘중 어떤 방법이 더 구조적으로 좋은지 궁금합니다.

답변 2

0

토비님의 프로필 이미지
토비
지식공유자

안녕하세요.

MemberFinder는 애플리케이션 서비스가 헥사곤 경계를 통해서 다른 모듈이나 클라이언트에 제공하는 기능으로 만들어진 것이죠. 단순 조회일 수도 있고, 그 이상의 로직(id에 해당하는 엔티티가 없으면 예외 발생 혹은 대체 작업 수행)을 담기도 합니다. 그렇게 애플리케이션 계층에서 서비스로 제공하는 것 중에서 조회 성격인 것들을 Finder로 옮긴 것이고요. 거기에 예외처리 기능까지 넣었으니 직접 리포지토리로 접근해서 확인하고 없으면 예외 발생시키고 하는 로직이 여기저기 중복되니 그걸 Finder가 제공하는 기능으로 리팩터링 한 것입니다.

물론 그러더라도 리포지토리에서 직접 조회해도 됩니다. 일반적으로 단순 조회인 경우엔 필요하면 어디서든 접근하게 해도 됩니다. 하지만 Finder로 분류되는 외부 제공 + 기능 추가 로직이 있다면 이를 쓰도록 만드는 것이 유리하겠죠.

프로필로 조회하는 기능을 이용해서 만든 로직이 나중에 여러 모듈이나 액터에서 필요해지면 Finder에 담을 수도 있습니다만, 그래도 위의 중복 체크 로직은 리포지토리를 그대로 둘 수도 있고요. 아마도 등록할 때 체크하는 것과 유저의 입력을 통해서 조회하고 없는 경우 의미있는 예외나 리턴 값을 만드는 조회 직후의 로직이 다르기 때문일 겁니다. 혹시 중복 체크로직에서도 같은 조회 결과를 처리하도록 만든다면, 나중에 Finder에 넣은 기능을 사용하도록 리팩터링 해도 됩니다. 이건 이후 개발을 진행하면서 다뤄보도록 하겠습니다.

login() 예로 드신 부분은 초기에 기능을 추가하고 테스트 한 뒤에 로그인과 관련된 기능을 추가하면서(로그인 직후에 여러가지 후속 작업이 계속 추가되는 경우가 많겠죠) 어떻게 리팩터링할지에 따라서 달라질 겁니다.

그래도 나름의 원칙을 잡아보자면

save()를 수행하는 것이 그 엔티티를 다루는 모듈 외부에서는 일어나지 않게 해야 합니다. 예를 들어 수강을 했을 때 회원의 무슨 상태를 변경한다고 하면, 수강 서비스에서 직접 회원 정보를 고치고, save() 하는 것은 바람직하지 않습니다. 이 때는 회원 정보의 변경을 다루는 애플리케이션 서비스에 로직을 담당하는 기능을 만들고 이 내부에서 처리하게 해야 합니다. 안 그러면 변경이 사방에서 일어나고 나중에 추적하기도 매우 어렵게 만듭니다.
따라서 저 login()이 Member 서비스가 아닌 다른 인증 모듈에서 일어나는 것이고, 로그인 처리 결과를 리턴하는 것 외에 Member에 어떤 변화가 필요하다면 Member쪽 인터페이스를 통해서 그 부분을 처리하게 하는게 좋습니다. 단순히 로그인 시간의 변경 이상의 작업이 미래에 일어날 수도 있기 때문이죠.
반면 login()이 Member의 로직을 다루는 내부에서 처리한다면 직접 접근하는 코드로 만들어도 됩니다. 물론 로그인 성공 이후 작업을 처리하는 코드가 늘어날 수 있으니 저라면 우선 메소드는 추출해서 분리해두겠습니다.

파트 2와 두번째 강의에서 이런 설계 문제에 대해서 계속 이야기해볼 생각입니다.

rhdrlehf5님의 프로필 이미지
rhdrlehf5
질문자

감사합니다!

 

0

안녕하세요, 인프런 AI 인턴입니다. rhdrlehf5님!

현재 질문하신 내용은 저장소 접근 방식과 관련된 주제로, 특히 repository를 직접 접근할지 아니면 별도의 서비스 또는 Finder를 사용할지에 대한 고민이신 것 같습니다. 이 부분은 설계의 선택에 따라 달라질 수 있습니다.

직접 접근 vs 서비스/포트 통한 접근

  1. 직접 접근:

    • 장점: 코드가 간결해지고 즉시 데이터를 다룰 수 있습니다.
    • 단점: 레이어드 아키텍처의 이점을 놓치게 되고, 비즈니스 로직이 저장소 접근과 뒤섞일 수 있습니다.
  2. 서비스/포트 통한 접근:

    • 장점: 비즈니스 로직을 더욱 명확하게 분리하며 재사용성을 높일 수 있습니다. 특정 비즈니스 규칙에 따라 여러 저장소 접근을 묶어서 처리할 수 있습니다.
    • 단점: 일부 상황에서는 단순한 작업도 오버헤드가 발생할 수 있습니다.

예시에서의 접근 방법

  1. memberFinder vs 직접 repository 접근:

    • memberFinder: 보통 여러 저장소 조회나 복잡한 조회 로직을 추상화하여 사용하고, 비즈니스 로직과 저장소 접근 로직을 분리하는 데 효과적입니다.
    • 직접 접근: 상대적으로 단순한 조회나 특정 로직에서만 사용할 때 사용됩니다.
  2. 로그인 예시:

    • member.updateLastLoginAt()memberRepository.save(member): repository에 직접 접근하여 간결하게 멤버 업데이트 후 저장. 이는 매우 일반적인 패턴입니다.
    • memberUpdater.updateLastLoginAt(): 비즈니스 로직의 일부분으로, 여러 멤버와 관련 로직을 묶어서 더 복잡한 처리를 하는 경우, 이를 포트로 추상화하여 더 명확하게 관리할 수 있습니다.

기준

  • 비즈니스 로직이 복잡한가? 복잡하다면 서비스/포트로 분리해서 관리하는 것이 좋습니다.
  • 단순한 저장소 작업인가? 단순한 작업은 직접 접근이 편리할 수 있습니다.

결론적으로, 선택은 상황에 따라 달라지며, 유지보수성과 재사용성을 고려하여 접근 방식을 선택하는 것이 좋습니다.

관련된 강의 내용을 통해 비슷한 사례를 살펴보시면 더욱 풍부한 인사이트를 얻으실 수 있습니다.
- 토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

rhdrlehf5님의 프로필 이미지
rhdrlehf5

작성한 질문수

질문하기