# Chapter 7: 도메인 서비스

<br>

## 💡 책에서 기억하고 싶은 내용

<br>

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

* 도메인 영역의 코드를 작성하다 보면, 한 애그리거트로 구현할 수 없을 때가 있다
  * ex) 결제 금액 계산 로직
    * 주문 애그리거트가 필요한 데이터를 모두 가지도록 한 뒤, 할인 금액 계산 책임을 주문 애그리거트에 할당 하는 방법???
    * but, 이렇게 구현하면 애그리거트는 자신의 책임 범위를 넘어서는 기능을 구현하게 됨
    * 이런 문제를 해소 하기 위한 가장 쉬운 방법 → `도메인 기능을 별도 서비스로 구현`하는 것!

### 도메인 서비스

* `도메인 서비스` 는 도메인 영역에 위치한 도메인 로직을 표현할 때 사용한다
* 주로 다음 상황에서 도메인 서비스를 사용한다
  1. 계산 로직
     * 여러 애그리거트가 필요한 계산 로직
     * 한 애그리거트에 넣기에는 다소 복잡한 계산 로직
  2. 외부 시스템 연동이 필요한 도메인 로직
     * 구현하기 위해 타 시스템을 사용해야 하는 도메인 로직

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

* 할인 금액 규칙 계산처럼, 한 애그리거트에 넣기 애매한 도메인 개념을 구현하려면 `도메인 서비스`를 이용해서 `도메인 개념`을 명시적으로 드러내면 된다
* 도메인 서비스는 `상태 없이 로직만 구현` 한다
  * 도메인 서비스를 구현하는 데 필요한 상태는 다른 방법으로 전달받는다
* 도메인 서비스는 `도메인 의미가 드러나는 용어` 를 타입과 메서드 이름으로 갖는다
* 도메인 서비스를 사용하는 주체는 `애그리거트` 가 될 수도 있고, `응용 서비스` 가 될 수도 있다
* 애그리거트 객체에 도메인 서비스를 전달하는 것은 `응용 서비스 책임` 이다
* 애그리거트 메서드를 실행할 때 도메인 서비스를 인자로 전달하지 않고, 반대로 `도메인 서비스의 기능을 실행할 때` 애그리거트를 전달하기도 한다
  * ex) 계좌 이체
    * 두 계좌 애그리거트가 관여하는데, 하나는 출금하고 하나는 입금한다
    * 응용 서비스는 두 Account 애그리거트를 구한 뒤에 해당 도메인 영역의 TransferService를 이용해서 계좌 이체 도메인 기능을 실행한다
* 도메인 서비스는 `도메인 로직` 을 실행하지 `응용 로직` 을 수행하진 않는다
  * 트랜잭션 처리와 같은 로직은 응용 서비스에서 처리해야 한다!!

### 특정 기능이 응용 서비스인지 도메인 서비스인지 감을 잡기 어려울 때

* 해당 로직이 애그리거트의 `상태를 변경` 하거나 애그리거트의 `상태 값을 계산하는지` 검사해 보면 된다!
* 위의 둘에 해당하는 도메인 로직이면서, 한 애그리거트에 넣기 적합하지 않으면 `도메인 서비스`!

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

* `외부 시스템`이나 `타 도메인과의 연동 기능`도 도메인 서비스가 될 수 있다

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

* 도메인 서비스는 `도메인 로직` 을 표현하므로, 도메인 서비스의 위치는 다른 도메인 구성요소와 `동일한 패키지` 에 위치한다
  * 도메인 서비스는 도메인 영역에 위치한다!
* 도메인 서비스 개수가 많거나, `엔티티` 나 `밸류` 와 같은 다른 구성요소와 `명시적으로 구분` 하고 싶다면, domain 패키지 밑에 domain.model, domain.service, domain.repository와 같이 `하위 패키지를 구분` 하여 위치시켜도 된다!

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

* 도메인 서비스의 로직이 고정되어 있지 않은 경우, 도메인 서비스 자체를 `인터페이스` 로 구현하고, 이를 구현한 클래스를 둘 수도 있다
* 특히 도메인 로직을 `외부 시스템`이나 `별도 엔진`을 이용하여 구현할 때 인터페이스와 클래스를 분리하게 된다!
  * 도메인 서비스의 구현이 특정 기술에 종속되면, 인터페이스와 구현 클래스로 `분리` 한다
