반응형
여러 애그리거트가 필요한 기능
- 도메인 영역의 코드를 작성하다 보면 한 애그리거트로 기능을 구현할 수 없을 때가 있다.
- 여러 애그리거트로 로직을 구현하게 되면 어떤 애그리거트가 주체인지 애매하다.
- 만약 한 애그리거트가 주체로 구현할 경우 해당 애그리거트는 책임 범위를 넘어서는 기능을 구현하기 때문에 코드가 길어지고 외부에 대한 의존이 높아지게 된다.
- 게다가 애그리거트의 범위를 넘어서는 도메인 개념이 애그리거트에 숨어들어서 명시적으로 드러나지 않게 된다.
- 이런 문제를 해소하기 위해 도메인 서비스를 별도로 구현한다.
도메인 서비스란?
- 한 애그리거트에 넣기 애매한 도메인 개념을 구현하려면 애그리거트에 억지로 넣기보다는 도메인 서비스를 이용하여 도메인 개념을 명시적으로 드러내면 된다.
- 도메인 서비스는 상태가 없이 로직만 구현한다.
- 예를 들어, 주문 시 결제금액 할인(쿠폰, 회원 등급에 따른 할인 등등..) 후 최종 결제금액을 계산하는 기능이 있다고 하자.
// 도메인 서비스 사용 예시
public class OrderService {
// 할인 금액 계산 도메인 서비스
private final DiscountCalculationService discountCalculationService;
private final OrderRepository orderRepository;
private final MemberRepository memberRepository;
@Transactional
public OrderNo placeOrder(OrderRequest orderRequest) {
OrderNo orderNo = orderRepository.nextId();
Order order = createOrder(orderNo, orderRequest);
orderRepository.save(order);
return orderNo;
}
private Order createOrder(OrderNo orderNo, OrderRequest orderReq) {
Member member = memberRepository.findById(orderReq.getOrdererId());
Order order = new Order(
orderNo,
orderReq.getOrderLines(),
orderReq.getCoupons(),
createOrderer(member),
orderReq.getShippingInfo()
);
// 결제금액 계산
order.calculateAmounts(this.discountCalculationService, member.getGrade());
return order;
}
}
도메인 서비스 객체를 애그리거트에 주입하지 않기
- 애그리거트의 메서드를 실행할 때 도메인 서비스 객체를 파라미터로 전달하는 것은 애그리거트가 도메인 서비스에 의존한다는 것을 뜻한다.
- 애그리거트가 의존하는 도메인 서비스를 의존 주입으로 처리하고 싶어질 수 있다.
- 하지만 이 방법은 좋은 방법은 아니라고 생각한다.(최범균님의 생각)
// 애그리거트에서 도메인 서비스를 의존 주입한 예시
public class Order {
@Autowired
private DiscountCalculationService discountCalculationService;
...
}
- 애그리거트에 데이터를 담는 필드는 모델에서 중요한 구성 요소이다.
- 그런데 도메인 서비스는 이와는 전혀 상관없을 뿐더러 도메인 서비스의 모든 기능을 사용하기 위해서가 아닌 일부 기능만 사용하므로 굳이 도메인 서비스 객체를 애그리거트에 의존 주입할 이유는 없다.
도메인 서비스에서 기능을 실행
- 애그리거트 메서드를 실행할 때 도메인 서비스를 인자로 전달하지 않고 반대로 도메인 서비스의 기능을 실행할 때 애그리거트를 전달하기도 한다.
- 예를 들어, 계좌 이체 기능이 이에 해당한다.
// 계좌 이체 기능 예시
public class TransferService {
public void transfer(Account fromAcc, Account toAcc, Money amounts) {
fromAcc.withdraw(amounts);
toAcc.credit(amounts);
}
...
}
- 도메인 서비스는 도메인 로직을 수행하지 응용 로직을 수행하지는 않는다.
- 트랜잭션 처리와 같은 로직은 응용 로직이므로 도메인 서비스가 아닌 응용 서비스에서 처리해야 한다.
도메인 서비스의 패키지 위치
- 도메인 서비스는 도메인 로직을 실행하므로 도메인 서비스의 위치는 다른도메인 구성 요소와 동일한 패키지에 위치한다.
- order
- application
- OrderService
- domain
- OrderRepository
- Order
- DiscountCalculationService
- application
- 다른 구성요소와 명시적으로 구분하고 싶다면 domain 패키지 밑에 domain.model, domain.service, domain.repository와 같이 하위 패키지를 구분해서 위치시켜도 된다.
도메인 서비스의 인터페이스와 클래스
- 도메인 서비스의 구현이 특정 구현 기술에 의존적이거나 외부 시스템의 API를 실행한다면 도메인 영역의 도메인 서비스는 인터페이스로 추상화 해야한다.
- 이때 인터페이스와 구현 클래스로 구분한다.
- domain
- Order
- DiscountCalculationService(Interface)
- infra
- RuleBasedDisCalSerivce
- domain
- 이를 통해 도메인 영역이 틀정 구현에 종속되는 것을 방지할 수 있고 도메인 영역에 대한 테스트가 수월해진다.
반응형
'DDD' 카테고리의 다른 글
응용(Application) 영역과 표현(UI) 영역 (0) | 2021.09.15 |
---|---|
도메인(Domain) 모델에 지켜야할것! (0) | 2021.09.15 |
애그리거트(Aggregate) 트랜잭션 관리 (0) | 2021.09.15 |
애그리거트(Aggregate)간에 참조 및 영속성 전파 (0) | 2021.09.15 |
DIP(역전 의존 원칙)란? (0) | 2021.09.14 |