[DDD START] 6장 응용 서비스와 표현 영역 - 응용 서비스의 구현
21 Jul 2019응용 서비스의 구현
응용 서비스의 크기
응용 서비스는 보통 다음 두 가지 방법 중 한 가지 방식으로 구현한다
- 한 응용 서비스 클래스에 회원 도메인 모든 기능 구현하기
- 장점
- 코드 중복 제거
- 단점
- 클래스의 크기가 커짐
- 장점
- 구분되는 기능별로 응용 서비스 클래스를 따로 구현하기
- 장점
- 코드의 품질을 일정 수준 유지
- 클래스별로 필요한 의존 객체만 포함
- 단점
- 클래스의 개수 증가
- 중복 코드는 별도의 클래스에 로직을 구현해서 방지 ex) MemberServiceHelper
- 장점
응용 서비스의 인터페이스가 필요한가?
- 구현 클래스가 다수 존재하거나 런타임에 구현 객체를 교체해야 하는 경우는 매우 드물다
- 소스 파일만 많아지고 구현 클래스에 대한 간접 참조가 증가해서 구조만 복잡해진다
- 명확하게 필요하기 전까지는 응용 서비스에 대한 인터페이스는 좋은 설계라고 볼 수 없다
응용 서비스는 표현 영역에서 필요한 데이터만 리턴하는것이 좋다
- 응용 서비스에서 애그리거트 자체를 리턴하면 도메인 로직의 실행을 응용 서비스와 표현 영역 두 곳에서 할 수 있게 된다
- 기능 실행 로직을 분산시켜 코드의 응집도를 낮추는 원인이 된다
표현 영역에 의존하지 않는다
- 응용 서비스만 단독의 테스트하기 힘들다
- 표현 영역의 변경이 응용 서비스에도 영향을 미친다
- 응용 서비스가 표현 영역의 역할까지 대신할 수 있어 표현 영역의 응집도가 깨진다
도메인 영역에서 발생시킨 이벤트를 처리한다
예)암호 초기화 기능은 다음과 같이 암호 변경 후 “암호 변경됨” 이벤트를 발생시킬 수 있다
public class Member {
private Password password;
public void initializePassword() {
String newPassword = generateRandomPassword();
this.password = new Password(newPassword);
Events.raise(new PasswordChangedEvent(this.id, password))
}
}
응용 서비스는 이벤트를 받아서 후처리를 한다
public class InitPasswordService {
@Transactional
public void initializePassword(String memberId) {
Events.handle((PasswordChangeEvent evt) -> {
// evt.getId() 에 해당하는 회원에게 이메일 발송하는 기능 구현
});
}
}
왜 이벤트를 사용하지?
- 도메인 간 의존성이나 외부 시스템의 의존성을 낮출 수 있다
- 시스템을 확장하는데 이벤트가 핵심 역할을 한다
이벤트를 사용하지 않은 코드
public class InitPasswordService {
@Transactional
public void initializePassword(String memberId) {
Member member = memberRepository.findById(memberId);
checkMemberExists(member);
member.initializePassword(); // 이벤트 발생하지 않음
sendNewPasswordMailToMember(member); // 이메일 발송
}
}
응용 서비스가 사용자 요청 기능을 실행하는데 별다른 기여를 못한다면 응용 서비스를 만들지 않고 바로 도메인 리포지토리에 접근해도 된다
- 서비스에서 수행하는 추가 로직이 없고
- 조회 전용 기능이어서 트랜잭션이 필요하지 않을 때
public class OrderController {
private OrderViewDao orderViewDao;
@RequestMapping("/myorders")
public String list(ModelMap model) {
String OrdererId = SecurityContext.getAuthentication().getId();
List<OrderView> orders = orderViewDao.selectByOrderer(ordererId);
}
}