[DDD] 도메인 주도 개발 시작하기 - 7장

Y_Sevin·2023년 8월 26일
0

7.1 여러 애그리거트가 필요한 기능

  • 도메인 영역의 코드를 작성하다 보면, 한 애그리거트로 기능을 구현할 수 없을 때가 있음
  • 한 애그리거트에 넣기 애매한 도메인 기능을 억지로 특정 애그리거트에 구현하면 안됨
    - 도메인 서비스를 별도로 구현하는 방법으로 문제를 해소할 수 있음

7.2 도메인 서비스

  • 도메인 서비스는 도메인 영역에 위치한 도메인 로직을 표현할 때 사용함
    - 계산 로직: 여러 애그리거트에 걸친 계산 로직, 한 애그리거트에 넣기 복잡한 계산 로직
    - 외부 시스템 연동이 필요한 도메인 규칙: 구현하기 위해 타 시스템을 사용해야 하는 도메인 로직

7.2.1 계산 로직과 도메인 서비스
한 애그리거트에 넣기 애매한 도메인 개념을 구현하기 위해서는 애그리거트에 억지로 넣기보단 도메인 서비스를 이용해서 도메인 개념을 명시적으로 드러내면 됨

도메인 서비스란
구현한 로직을

//할인 금액 계산 로직을 위한 도메인 서비스
public class DiscountCalculationService {

  public Money calculateDiscountAmounts(
      List<OrderLIne> orderLines,
      List<Coupon> coupons,
      MemberGrade grade) {
    Money couponDiscount = coupons.stream()
        .map(coupon -> calculateDiscount(coupon))
        .reduce(Money(0), (v1, v2) -> v1.add(v2));

    Money membershipDiscount = calculateDiscount(orderer.getMember().getGrade());

    return couponDiscount.add(membershipDiscount);
  }
}
// 도메인 서비스를 사용하는 주체는 애그리거트 객체 또는 응용서비스가 될 수 있음
public class Order {
 
    public void calculateAmounts(DiscountCalculationService disCalSvc, MemberGrade grade) {
        Money totalAmounts = getTotalAmounts();
        Money discountAmounts = disCalSvc.calculateDiscountAmounts(this.orderLines, this.coupons, grade);
        this.paymentAmounts = totalAmounts.minus(discountAmounts);
    }
}
//응용 서비스는 애그리거트 객체에 도메인 서비스를 전달한다.
public class OrderService {
    private DiscountCalculationService discountCalculationService;

    @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 = findMember(orderReq.getOrdererId());
    	Order order = new Order(orderNo, orderReq.gerOrderLines(),
    	        orderReq.getCoupons(), createOrderer(member),
    	        orderReq.getShippingInfo());
    	order.calculateAmounts(this.discountCalculationService, member.getGrade());
    	return order;
    }
    ...
}

도메인 서비스 객체를 애그리거트에 주입하지 않기
애그리거트 메서드를 실행할 때, 도메인 서비스 객체를 파라미터로 전달하는 것은, 애그리거트가 도메인 서비스에 의존한다는 것을 의미함.
때문에 애그리거트가 의존하는 도메인 서비스를 스프링 DI를 통해 의존 주입할 수 있지만 좋은 방법은 아님
도메인 객체는 필드와 메서드를 이용해서 개념적으로 하나인 모델을 의미함
하지만 discountCalculateSerivce 필드는 데이터 자체와는 관련이 없고, DB에 저장 대상도 아님..
또한, Order가 제공하는 모든 기능에서 discountCalculateService를 쓰는 것도 아님
때문에 일부 기능을 위해 굳이 도메인 서비스 객체를 애그리거트에 의존 주입할 이유가 없음

  • 애그리거트 메서드를 실행할 때 도메인 서비스를 인자로 전달하지 않고 도메인 서비스의 기능을 실행할 때 애그리거트를 전달하기도 함
//계좌 이체 도메인 서비스 - 기능을 실행할 때 애그리거트 전달
public class TransferService {

  public void transfer(Account fromAcc, Account toAcc, Money amounts) {
    fromAcc.withdraw(amounts);
    toAcc.credit(amounts);
  }

}

도메인 서비스는 도메인 로직을 수행하지 응용 로직을 수행하지는 않음
-> 그러므로 트랜잭션 처리와 같은 로직은 응용 로직이므로 도메인 서비스가 아닌 응용 서비스에서 처리

응용 서비스와 도메인 서비스의 구분이 어렵다면 애그리거트의 상태를 변경하거나 상태값을 계산하는지 확인

7.2.2 외부 시스템 연동과 도메인 서비스

  • 외부 시스템이나 타 도메인과의 연동 기능도 도메인 서비스가 될 수
  • 도메인 서비스의 구현이 특정 구현 기술에 의존하거나 외부 시스템의 API 를 실행한다면 도메인 영역의 서비스는 인터페이스로 추상화해야하며 인프라스트럭처영역에 위치시켜야 함
profile
매일은 아니더라도 꾸준히 올리자는 마음으로 시작하는 개발블로그😎

0개의 댓글