# 7. 도메인 서비스

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

  • 한 애그리거트로는 표현하기 애매한 부분이 생길 수 있음
  • ex) 총 주문 금액(상품, 주문, 할인 쿠폰, 회원 할인 등...)
  • 이렇게 애매한 도메인 기능을 특정 애그리거트에 억지로 구현하면 안됨

→ 도메인 기능을 별도 서비스로 구현

# 7.2 도메인 서비스

다음과 같은 상황에 도메인 서비스 사용

  • 계산 로직 : 여러 애그리거트가 필요한 계산 로직, 한 애그리거트에 넣기에는 다소 복잡한 계산 로직
  • 외부 시스템 연동이 필요한 도메인 로직 : 구현하기 위해 타 시스템을 사용해야 하는 도메인 로직

# 7.2.1 계산 로직과 도메인 서비스

  • 응용 영역의 서비스 → 응용 로직
  • 도메인 서비스 → 도메인 로직
  • 도메인 서비스는 상태 없이 로직만 구현
  • ex) 할인 금액 계산 로직 도메인 서비스
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(grade);
        
        return couponDiscount.add(membershipDiscount);
    }
    
    private Money calculateDiscount(Coupon coupon) {
        // 쿠폰에 따른 할인 금액 계산 로직
    }
    
    private Money calculateDiscount(MemberGrade grade) {
        // 회원 등급에 따른 할인 금액 계산 로직
    }
}
  • 도메인의 의미가 드러나는 용어를 타입과 메서드 이름으로 가짐
  • 해당 도메인 서비스를 애그리거트 객체에 전달
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);
    }
    ...

# 도메인 서비스 객체를 애그리거트에 주입해도 될까?

  • 필자는 추천하지 않는 방법
  • 도메인 객체에서 필드는 데이터를 담는 기능인 중요한 구성요소
  • 그런데 도메인 서비스는 데이터 자체와는 관련이 없음 + 저장 대상도 아님
  • 굳이 의존 주입할 이유가 없음

반대로 도메인 서비스를 이용할 떄 애그리거트를 전달하기도 함

중요한 건, 도메인 서비스는 도메인 로직만을 수행하지, 응용 로직(e.g. 트랜잭션 처리, 애그리거트의 상태 변경)을 수행해서는 안됨

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

public interface SurveyPermissionChecker {
    boolean hasUserCreationPermission(String userId);
}
  • 외부 시스템과 HTTP로 소통 → 도메인 입장에서는 도메인 로직으로 볼 수 있음
  • 인터페이스(도메인)와 구현체(인프라)로 구현

# 7.2.3 도메인 서비스의 패키지 위치

  • 도메인 서비스의 개수가 많거나 다른 구성요소와 명시적으로 구분하고 싶다면 domain 패키지 밑에 domain.model, domain.service, domain.repository 등으로 구분하여 위치시켜도 됨

# 7.2.4 도메인 서비스의 인터페이스와 클래스

  • 도메인 서비스의 구현이 특정 기술에 의존하면 인터페이스 + 구현체 (추상화)
  • 도메인 영역이 특정 구현에 종속되는 것을 방지할 수 있음