도메인 주도 개발 2장

김재연·2025년 4월 6일
post-thumbnail

네 개의 영역



영역을 다음과 같이 표현할 수 있다.

이전 장에서 다루었던 것은 도메인 계층이고, 응용 서비스가 이를 적합하게 활용해, 로직을 수행한다. (비즈니스 로직을 도메인 측에 위임한다는 것이 중요)

계층 구조 아키텍처

보통 계층은 위와 같지만, 이례적으로 다음과 같은 경우도 존재한다.

이와 같은 경우 문제가 어떤 문제가 발생하게 될까?

서비스가 인프라스트럭처(이전 이미지에서 보았던 것처럼 외부의 요소와 연결되있음) 영역을 참조하게 되고, 둘이 강하게 연결된다.

이로 인해, 테스트 하기가 힘들어진다는 단점이 있다.

실제로 나의 경우에서는, 이미지를 업로드 하는 기능 통합 테스트를 진행할 때, 이로 인해서 어려움을 많이 겪었었다.

두번째 단점은, 구현 방식을 변경하기 힘들다는 것이다.

Spring을 사용하며 보통 JPA를 많이 사용하지만, MyBatis와 같은 다른 기술을 사용한다고 했을 때, Service 부분에서 인프라스트럭처 부분을 참조하고 있다면 변경해야 할 코드가 많아진다.

이를 해결하기 위해 어떻게 할 수 있을까?

DIP

고수준 모듈과 저수준 모듈을 다음과 같이 나눌 수 있다.

현재 문제는 고수준 모듈이 저수준 모듈을 참조하고 있다는 것이 문제이다.

이 참조 방향을 반대로 바꾸면 괜찮아지지 않을까? 라는 것에서 나온 것이, 의존 역전 원칙(DIP)이다.

다음과 같이, 고수준 모듈에 인터페이스를 위치시켜, 저수준 모듈에서 이를 구현하게 되면, 의존 방향이 반대로 흐르게 된다.

이렇게 됨으로써, 고수준 모듈은 저수준 모듈 관련 코드를 정확하게 알 필요가 없고, 또한 구현을 변경할 때도 주입만 다른 객체를 구현 해주면 되는 것이다.

자연스레, 테스트 문제도 해결이 된다.
구현을 변경할 때, 구현체만 바꿔주면 되었듯이, 테스트할 때도 테스트에 맞춰 구현체를 변경해 주기만 하면 되는 것이다.

DIP 주의 사항

이는 사실 의존 역전 원칙을 잘못 적용한 모습이다.
이는 참조 관계를 전혀 개선하지 못한 것을 볼 수 있다.

이와 같이 말이다.

즉, 인터페이스는 고수준 모듈에 두어야 한다.

DIP와 아키텍처

이렇게 저수준 모듈이 고수준 모듈을 참조하게끔 바꾸게 되면, 도메인과 응용 영역에 대한 영향을 주지 않거나 최소화하면서 구현 기술을 변경하는 것이 가능하다.

위 그림에서 EmailNotifier이 아닌 SMSNotifier를 사용해야 한다고 하면, 새로운 구현체를 만들고, OrderService에서는 구현체만 바꿔주면 되는 것이다. Spring의 의존성 주입을 활용하게 되면 Service의 코드를 변경할 필요가 전혀 없다.

도메인 영역의 주요 구성요소

엔티티와 밸류

DB 테이블의 엔티티, 도메인 모델의 엔티티를 구분하는 것이 중요하다고 한다.

이 두 모델의 가장 큰 차이점은 도메인 모델의 엔티티는 데이터와 함께 도메인 기능도 함께 제공한다는 것이다.

애그리거트

도메인이 커질수록 도메인 모델도 커지면서 굉장히 많은 엔티티와 밸류가 출현하게 되며 복잡도가 향상한다.

이때, 관련 객체를 하나로 묶어 애그리거트로 표현해 각각 배송지 정보, 주문자, 주문 목록, 총 결제 금액 등의 흩어져 있던 개념들을 주문으로 묶어, 이해를 도울 수 있다.

이를 통해, 개별 객체 간의 관계가 아닌, 애그리거트 간의 관계로 도메인 모델을 관리할 수 있다.

또한, 애그리거트 내에서 가장 상위 개념인 애그리거트 루트를 통해 외부로 기능을 제공해 캡슐화를 진행할 수 있다. (JPA의 연관관계 매핑을 통해서 쉽게 진행할 수 있습니다.)

리포지터리

결국 도메인 객체는 물리적인 저장소에 보관되어야 한다. 이를 고수준 모듈에서 제공하는 것이 Repository이다.

이를 통해서 애그리거트 단위로 저장 및 조회가 가능하다.

전체적인 모듈 구조는 다음과 같다.

요청 처리 흐름

지금까지 봐온 처리 흐름을 정리하면 위와 같다.

인프라스트럭처 개요

현재까지 살펴본 바로 인해, 무조건 인프라스트럭처에 대한 의존을 없애야 한다는 강박을 가지지 않아도 된다.
@Transactional, @Entity, @Table 같은 부분도 모두 인프라스트럭처와 관련된 부분이지만, 응용 계층, 도메인 계층에 존재한다.
오히려 이를 없애려고 노력하면 복잡도가 향상하게 되는 안 좋은 결과를 맞이하게 된다.

뭐든지 트레이드오프를 고려해 결정하는 것이 좋다는 것이다.

모듈 구성

애그리거트 단위로 모듈을 구분하였을 때, 다음과 같이 구분할 수 있다.

또한 각각의 애그리거트에 또 여러 하위 도메인이 존재한다면

패키지를 다음과 같이 구성할 수 있다.

모듈 구조를 얼마나 세분화할지는 정해진 기준은 없다고 한다.
하지만, 필자는 한 패키지에 10~15개 미만으로 타입 개수를 유지하려고 노력하고, 이 기준을 넘어가면 패키지를 분리한다고 한다.
이 또한 각자의 기준을 가지고 진행하는 것이 좋을 듯하다.

profile
끊임없이 '성장'하는 개발자 김재연입니다.

0개의 댓글