본 글에서는 도메인 서비스에 대해서 정리하고자 한다.
도메인 서비스와 애플리케이션 서비스의 차이점, 도메인 서비스를 사용할 때의 이점에 대해서도 설명하고자 한다.
1. Domain services와 Application services의 차이점
도메인 서비스는 entity와 VO에 자연스럽게 맞지 않는 도메인 지식을 제공하는 경우가 있다. 하지만 도메인 서비스를 도입하는 또다른 이유가 있다. 이는 도메인 모델 격리 (isolation)과 관련있다.
Domain Service, Application Service는 entity, VO를 상위에서 다루는 stateless한 클래스를 의미한다. 이렇게 보면 둘은 꽤 유사해 보인다. 그러나 도메인 서비스는 도메인 로직을 갖고 있지만, 애플리케이션 서비스는 그렇지 않다는 점에서 큰 차이점을 띈다.
2. Domain Service의 특징
- 비즈니스 의사결정과 관련된 코드를 담당한다.
- Entity / VO에 속할 수 없는 도메인 로직을 다룰 때 사용한다.
3. Domain Service 추출하기
도메인 지식은 도메인 서비스에 위임하고, 애플리케이션 서비스는 도메인 서비스의 오케스트레이션을 담당한다.
이러한 가이드라인을 따르는 코드들은 다음과 같은 패턴을 보인다.
- 비즈니스 로직을 실행할 때, 필요한 정보를 준비한다.
- 이는 데이터베이스, API 호출 등을 통해 준비한다.
- 비즈니스 로직을 실행한다.
- 1개 이상의 도메인 모델을 활용하여 비즈니스 의사결정을 실행한다. 이 과정을 통해 모델의 상태가 변경되거나, 필요한 정보가 생성된다.
- 비즈니스 로직의 실행 결과를 실제 세계에 반영한다.
- 데이터베이스에 실행 결과를 저장하거나, 외부 서비스를 요청하여 실행 결과를 실 세계에 반영한다.
예시
atm에서 돈을 출금하는 함수를 살펴보자.
돈을 출금하기 위해서는, 잔고를 확인한 후, PG 연동사에서 돈을 차감한 후에 돈을 출금해야한다. 이 때, PG 연동사에 돈을 차감하는 비즈니스 로직은 Entity, VO에 속할 수 없는 기능이다.
public void WithdrawMoney(decimal amount)
{
if (!_atm.CanDispenseMoney(amount))
return ;
decimal amountWithCommission = _atm.CalculateAmountWithCommission(amount);
Result result = _paymentGateway.ChargePayment(amountWithCommission);
if (result.IsFailure)
return ;
_atm.DispenseMoney(amount);
_repository.Save(_atm);
}
이와 같은 경우에는 Domain Service가 필요하다. 다음과 같이 비즈니스 로직은 도메인 서비스로 분리하고, Application Service는 프로세스의 오케스트레이션만 담당한다.
public void WithdrawMoney(decimal amount)
{
Atm atm = _repository.Get();
_atmService.WithdrawMoney(atm, amount);
_repository.Save(_atm);
}
public sealed class AtmService // Domain service
{
public void WithdrawMoney(Atm atm, decimal amount)
{
if (!atm.CanDispenseMoney(amount))
return ;
decimal amountWithCommission = atm.CalculateAmountWithCommission(amount);
Result result = _paymentGateway.ChargePayment(amountWithCommission);
if (result.IsFailure)
return;
atm.DispenseMoney(amount);
}
}
4. Domain Service의 장점
4-a. 도메인 서비스를 분리하면 Unit Test를 작성하기 편하다.
외부 의존성이 적기 때문에 Unit Test 작성 시, 의존성 주입을 위한 Mocking 처리를 줄일 수 있다.
4-b. 도메인 지식이 응집되어 있어 가독성이 좋다.
도메인 지식이 도메인 모델과 근접한 곳에 모여있기 때문에 코드를 유지보수하기 편하고, 가독성도 높아진다.
참고
'소프트웨어-이야기 > 프로그래밍 언어와 프레임워크' 카테고리의 다른 글
Spring과 의존성 주입 (DI) (0) | 2022.04.18 |
---|---|
Hibernate Query Plan Cache란? (0) | 2022.03.06 |
Hibernate-type-52 - Entity에 Postgresql Jsonb 타입 선언하기 (0) | 2020.11.21 |
[Django] Django에서 MultiProccess 사용 시 주의사항 (0) | 2020.03.08 |
(Django) Django에서 Thread를 다룰 때 주의할 점 (0) | 2020.01.27 |