해결된 질문
작성
·
15
·
수정됨
0
안녕하세요, 토비님.
강의 유익하게 듣고 있습니다.
🔵수업:
28. 회원 애플리케이션 서비스 테스트 (2) / 13:00~ : 이메일 중복 검사는 도메인모델에서 처리할 수 없음
🔵질문:
이메일 중복 여부의 검사는 도메인 규칙으로 생각됩니다. 이메일 중복 여부 검사를 도메인 계층 대신 애플리케이션 계층에 두신 점에 대해 토비님의 시각이 궁금합니다.
회원 도메인의 email 속성을 자연키로 삼아 유일성을 부여했고, 이는 도메인 규칙이라 생각됩니다.
이메일 중복 여부 검사는 외부 액터와의 상호작용이 필요하니 말씀하신 것처럼 도메인 모델 내부에서 직접 수행할 수 없지만, PasswordEncoder처럼 도메인 서비스를 통해 검사를 수행하여 '이메일은 중복될 수 없다'는 도메인 규칙을 도메인 계층에 잡아두는 것도 아키텍처 구조 상 문제는 없을 것 같습니다.
🔵코드:
수업 버전 - 애플리케이션 서비스에서 이메일 중복 검사
(코드 블럭을 java 코드로 설정했고, 글 작성/수정 페이지에서는 코드 색상이 제대로 표시되는데 수정을 완료하면 흑백으로 돌아옵니다.)
@Service
@RequiredArgsConstructor
public class MemberService implements MemberRegister {
private final MemberRepository memberRepository;
private final EmailSender emailSender;
private final PasswordEncoder passwordEncoder;
@Override
public Member register(Member.MemberRegisterRequest registerRequest) {
// 이메일 중복 검사
checkDuplicateEmail(registerRequest);
Member member = Member.register(registerRequest, passwordEncoder);
memberRepository.save(member);
sendWelcomeEmail(member);
return member;
}
...
}
회원 도메인 객체에서 이메일 중복 검사
도메인 서비스 EmailDuplicationChecker를 추가하고 Member.register(...)에서 EmailDuplicationChecker를 이용해 이메일 중복 검사 수행
domain.EmailDuplicationChecker
package tobyspring.splearn.domain;
/**
* 이메일 중복 검사를 수행하는 도메인 서비스
*/
public interface EmailDuplicationChecker {
boolean isDuplicated(String email);
}domain/Member
@Entity
...
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
public static Member register(
MemberRegisterRequest registerRequest,
PasswordEncoder passwordEncoder,
EmailDuplicationChecker emailDuplicationChecker
) {
Member member = new Member();
// 🔻이메일 중복 검사
if (emailDuplicationChecker.isDuplicated(registerRequest.email())) {
throw new DuplicateEmailException("Duplicated email: " + registerRequest.email());
}
...
return member;
}
...
}
레포지터리와 협력하기위해 EmailDuplicationChecker 구현하는 어댑터 추가
adapter.persistence.EmailDuplicationCheckerImpl
@Component
@RequiredArgsConstructor
public class EmailDuplicationCheckerImpl implements EmailDuplicationChecker {
private final MemberRepository memberRepository;
@Override
public boolean isDuplicated(String email) {
return memberRepository.findByEmail(new Email(email)).isPresent();
}
}
기존 애플리케이션 서비스에 포함된 이메일 중복 검사 제거
application.MemberService
@Service
...
public class MemberService implements MemberRegister {
private final MemberRepository memberRepository;
private final EmailSender emailSender;
private final PasswordEncoder passwordEncoder;
private final EmailDuplicationChecker emailDuplicationChecker;
@Override
public Member register(Member.MemberRegisterRequest registerRequest) {
// 🔻이메일 중복 검사 호출 제거
Member member = Member.register(registerRequest, passwordEncoder, emailDuplicationChecker);
memberRepository.save(member);
sendWelcomeEmail(member);
return member;
}
...
}
🔵 제가 생각해본 장단점
수업 버전 - 애플리케이션 서비스에서 이메일 중복 검사
장점
회원 등록 과정 절차에 이메일 중복 검사가 표현되어 회원 등록 과정 절차를 명시적으로 표현
'중복 검사는 안하나?' 같은 의문을 방지
단점
-
회원 도메인 객체에서 이메일 중복 검사
장점
이메일 중복 불가 규칙을 도메인 계층 내부에 표현하여 도메인 모델을 코드에 자세히 표현
여러 지점에서 중복 검사가 필요한 경우, 반복되는 중복 검사 메서드(레포지터리 조회 후 예외 생성)를 어댑터에 위임할 수 있음
단점
액터와의 협력이 필요한 도메인 규칙마다 도메인서비스&어댑터가 추가되어 유지보수 포인트가 추가되고, 이를 이용하는 객체에도 의존성이 추가되어 DI해야할 파라미터가 늘어남.
만약 제가 개발을 진행 한다면 도메인 규칙마다 도메인서비스&어댑터를 추가하는 비용이 크다고 생각하는 점, 도메인 계층까지 내려가지 않고도 로직의 큰 흐름을 이해할 수 있는 점에서 수업 내용대로 애플리케이션 서비스에 로직을 구현하는 방식을 사용할 것 같습니다. 그럼에도 도메인 규칙을 도메인 계층에 두는 것에 대해서 토비님이 의견이 궁금합니다.
감사합니다.
답변 1
1
말씀하신 대로 회원의 이메일은 중복되지 않는다는 도메인 지식을 인터페이스로 만들어 도메인 서비스로 정의하고, 메소드 호출 주입 방식을 써서 필요할 때 엔티티에 전달해서 생성 로직 과정중 검증 기능으 사용하게 만들 수 있습니다.
이런 경우 도메인 규칙이 도메인 모델을 직접 반영한 코드에 더 명시적으로 드러나고 풍부한 모델로서 가치가 있습니다.
다만, 이번 강의에서는 제가 이메일 중복을 체크하는 것은 같은 도메인 로직을 담당하지만 도메인 계증 내부가 아닌 애플리케이션 서비스에 두는 방식을 선택했습니다. 우선 외부 자원을 이용하는 시스템에 의존해야 하고, 애플리케이션이 포트를 통해 제공하는 기능의 흐름에 절차적으로 들어가기에 적당하다고 생각을 했고요. 다른 도메인 로직에 비해 단순 조회를 이용한 검증 로직이라서 도메인 서비스까지 동원해서 작성할 필요가 있을까라고 생각을 했습니다.
물론 리포지토리를 이용해서 동작하는 기능이 내부에 들어갈 수 있다고 해서 도메인 서비스로 만들면 안 되는 것은 아닙니다. 복잡한 도메인 규칙, 예를 들어 포인트 정책이나 결제 한도 계산 등의 도메인 로직은 구현이 자주 바뀌고 다양한 외부 리소스에 의존할 가능성이 높지만, 이건 도메인 모델로 표현되는 핵심적인 기능이기 때문에 이런 경우엔 도메인 서비스로 만들어 사용할 것입니다. 이때 도메인 모델 내부에서 동작하는 코드가 런타임시에 외부 리소스까지 연결되기 때문에 발생하는 복잡성 보다 중요한 핵심 도메인 로직을 처리하는 코드이기 때문에 도메인 모델 내에 두고, 개발팀 내에서 소통하고 테스트로 검증하는 등의 작업이 더 가치를 줄 수 있기 때문에 그런 선택을 하게 되는 것이죠.
제가 이번엔 그런 선택을 했지만, 만약 개발팀이 아직 도메인 서비스를 작성하는 것에 익숙하지 않고, 메소드 주입을 이용해서 도메인 서비스를 사용하면서 그 구현은 도메인 외부에 작성하는 방식의 접근 방법을 한번 손쉽게 적용하는 연습을 해본다면, 그때는 이메일 중복 체크를 위한 도메인 서비스를 따로 만들어 사용했을 것입니다. 이 접근 방법도 꽤 여러가지가 있습니다. 이어지는 강의에서 이에 대해서 여러가지 접근 방법을 한번 보여드리도록 할게요.
답변 감사합니다.
도메인 서비스로 두는 방법도 가능하나, 로직의 단순함과 회원 등록 절차의 흐름을 고려해 애플리케이션 서비스에 구현하신 것으로 이해했습니다.
이번 강의를 듣다보니 이어지는 강의도 기대됩니다.
좋은 하루 되시길 바랍니다.