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);
}
}
도메인 서비스는 도메인 로직을 수행하지 응용 로직을 수행하지는 않음
-> 그러므로 트랜잭션 처리
와 같은 로직은 응용 로직이므로 도메인 서비스가 아닌 응용 서비스
에서 처리
응용 서비스와 도메인 서비스의 구분이 어렵다면 애그리거트의 상태를 변경하거나 상태값을 계산하는지 확인