본문 바로가기
스터디/도메인 주도 개발 시작하기

[도메인 주도 개발 시작하기] 06. 도메인 서비스 & 도메인 모델과 바운디드 컨텍스트

by 디토20 2022. 7. 16.
반응형

 

 

 

[도메인 주도 개발 시작하기] 06. 도메인 서비스 

& 도메인 모델과 바운디드 컨텍스트

 

 

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

도메인 영역의 코드를 작성하다 보면 한 애그리거트로 기능을 구현할 수 없을 때가 있다. 대표적인 예가 결제 금액 계산 로직인데, 실제 결제 금액을 계산할 때는 다음과 같은 내용이 필요하다.

  • 상품 애그리거트: 구매하는 상품의 가격이 필요하다. 또한 상품에 따라 배송비가 추가되기도 한다.
  • 주문 애그리거트 : 상품별로 구매 개수가 필요하다.
  • 할인 쿠폰 애그리거트: 쿠폰 별로 지정한 할인 금액이나 비율에 따라 주문 총 금액을 할인한다.
  • 회원 애그리거트: 회원 등급에 따라 추가 할인이 가능하다.

이 상황에서 생각해 볼 수 있는 방법은 주문 애그리거트가 필요한 데이터를 모두 가지도록 한 뒤 할인 금액 계산 책임을 주문 애그리거트에 할당하는 것이다. 외부 애그리거트의 로직이 필요할 경우에는 도메인 기능을 별도의 서비스로 구현할 수 있다.

 

 

 

 

2. 도메인 서비스

도메인 서비스는 도메인 영역에 위치한 도메인 로직을 표현할 때 사용한다. 주로 다음 상황에서 도메인 서비스를 사용한다.

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

 

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

도메인 영역의 애그리거트나 밸류와 같은 구성요소와 도메인 서비스를 비교할 때 다른 점은 도메인 서비스는 상태 없이 로직만 구현한다는 점이다. 도메인 서비스를 구현하는 데 필요한 상태는 다른 방법으로 전달받는다. 도메인 서비스를 사용하는 주체애그리거트가 될 수도 있고 응용 서비스가 될 수도 있다.

 

* 주의점

- 도메인 서비스 객체를 애그리거트에 주입하지 않는다. 도메인 객체는 필드로 구성된 데이터와 메서드를 이용해 개념적으로 하나인 모델을 표현하기 때문에, 필요한 메서드에서만 파라미터로 받아서 쓰는 것이 좋다. 또는 도메인 서비스에 애그리거트를 파라미터로 전달하기도 한다.

 

 

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

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

 

 

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

도메인 서비스도메인 로직을 표현하므로 도메인 서비스의 위치는 다른 도메인 구성요소와 동일한 패키지에 위치한다.

도메인 서비스의 개수도메인 서비스가 많거나 엔티티나 밸류와 같은 다른 구성요소와 명시적으로 구분하고 싶다면 domain 패키지 밑에 domain.model, domain.service, domain.repository와 같이 하위 패키지를 구분하여 위치시켜도 된다.

 

 

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

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

 

도메인 서비스의 구현이 특정 구현 기술에 의존하거나 외부 시스템의 API를 실행한다면 도메인 영역의 도메인 서비스는 인터페이스로 추상화 하고 실제 구현은 인프라스트럭처 영역에 위치할 수 있다. 이를 통해 도메인 영역이 특정 구현에 종속되는 것을 방지할 수 있고 도메인 영역에 대한 테스트가 쉬워진다.

 

 

 

 

3. 도메인 모델과 경계

처음 도메인 모델을 만들 때 빠지기 쉬운 함정이 도메인을 완벽하게 표현하는 단일 모델을 만드는 시도를 하는 것이다. 그러나 하나의 도메인은 여러 하위 도메인으로 구분되기 때문에 이것은 불가능하다.

 

예를 들어 상품이라는 모델에서, 카탈로그에서의 상품, 재고 관리에서의 상품, 주문에서의 상품은 이름만 같지 실제로 의미하는 것은 다르다. 카탈로그에서의 상품은 상품 이미지, 상품명, 가격, 옵션 목록, 상세 설명과 같은 상품 정보가 위주라면 재고 관리에서는 실존하는 개별 객체를 추적하기 위한 목적으로 상품을 사용한다. 즉 카탈로그에서는 물리적으로 한개인 상품이 재고 관리에서는 여러개 존재할 수 있다.

 

또는 논리적으로 같은 존재이지만 하위 도메인에 따라 다른 용어를 사용하는 경우도 있다.

 

 

 

이렇게 하위 도메인마다 사용하는 용어가 다르기 때문에 올바른 도메인 모델을 개발하려면 하위도메인마다 모델을 만들어야 한다. 각 모델은 명시적으로 구분되는 경계를 가져서 섞이지 않도록 해야한다. 같은 제품이라도 카탈로그 컨텍스트의 제품과 재고 컨텍스트의 제품은 의미가 서로 다르다. 모델은 특정한 컨텍스트 하에서 완전한 의미를 갖는데, 이렇게 구분되는 경계를 갖는 컨텍스트를 DDD에서는 바운디드 컨텍스트라고 부른다.

 

 

 

4. 바운디드 컨텍스트

바운디드 컨텍스트모델의 경계를 결정하며 한 개의 바운디드 컨텍스트는 논리적으로 한개의 모델을 갖는다. 하위 도메인과 바운디드 컨텍스트가 일대일 관계를 갖는 것이 가장 이상적이지만, 현실은 그렇지 않을때가 많다. 용어를 명확하게 구분하지 못해 두 하위 도메인을 하나의 바운디드 컨텍스트에서 구현하기도 한다.

 

규모가 작은 기업은 전체 시스템을 한 개의 팀에서 구현할 때도 있다. 여러 하위 도메인을 하나의 바운디드 컨텍스트에서 개발할 때 주의할 점은 하위 도메인의 모델이 섞이지 않도록 하는 것이다. 하위 도메인마다 구분되는 패키지를 갖도록 구현해야 하며, 이렇게 함으로써 하위 도메인을 위한 모델이 서로 뒤섞이지 않고 하위 도메인마다 바운디드 컨텍스트를 갖는 효과를 낼 수 있다.

 

 

 

바운디드 컨텍스트는 도메인 모델을 구분하는 경계가 되기 때문에 구현하는 하위 도메인에 알맞은 모델을 포함한다. 같은 사용자라 하더라도 주문 바운디드 컨텍스트와 회원 바운디드 컨텍스트가 갖는 모델이 달라진다.

 

 

 

5. 바운디드 컨텍스트 구현

바운디드 컨텍스트도메인 모델 뿐만 아니라 도메인 기능을 사용자에게 제공하는 데 필요한 표현 영역, 응용 서비스, 인프라스트럭처 영역을 모두 포함한다. 도메인 모델의 데이터 구조가 바뀌면 DB 테이블 스키마도 함께 변경해야 하므로 테이블도 바운디드 컨텍스트에 포함된다.

 

모든 바운디드 컨텍스트를 반드시 도메인 주도로 개발할 필요는 없다. 상품의 리뷰는 복잡한 도메인 로직을 갖지 않기 때문에 CRUD 방식으로 구현해도 된다. 즉 DAO와 데이터 중심의 밸류 객체를 이용해서 리뷰 기능을 구현해도 기능을 유지 보수하는데 큰 문제가 없다.

 

 

한 바운디드 컨텍스트에서 두 방식을 혼합해서 사용할 수도 있다. 대표적인 예가 CQRS 패턴이다. 이 방식을 사용하면 상태 변경과 관련된 기능은 도메인 모델 기반으로 구현하고 조회 기능은 서비스-DAO를 이용해서 구현할 수 있다. 

 

각 바운디드 컨텍스트는 서로 다른 구현 기술을 사용할 수도 있다. 웹 MVC와 JPA를 사용하는 바운디드 컨텍스트가 존재할 수도 있고 Netty와 마이바티스를 이용한 바운디드 컨텍스트가 존재할 수도 있다.

 

 

 

 

6. 바운디드 컨텍스트 간 통합

REST API 호출을 통해 직접적으로 두 바운디드 컨텍스트를 통합할 수도 있고, 메시지 큐를 이용해 간접적으로 통합을 할 수도 있다. 메시지 큐를 이용할 경우 두 바운디드 컨텍스트가 사용할 메시지의 데이터 구조를 맞춰야한다.

 

두 바운디드 컨텍스트를 개발하는 팀은 메시징 큐에 담을 데이터의 구조를 협의하게 되는데 그 큐를 누가 제공하느냐에 따라 데이터 구조가 결정된다. 예를 들어 카탈로그 시스템에서 큐를 제공한다면 큐에 담기는 내용은 카탈로그 도메인을 따른다.

 

 

 

 

 

7. 바운디드 컨텍스트 간 관계

바운디드 컨텍스트는 다양한 방식으로 관계를 맺는데, 가장 흔한 관계를 한쪽에서 API를 제공하고 다른 한쪽에서 그 API를 호출하는 관계이다. REST API가 그 대표적인 예이다.

상류 컴포넌트는 API를 제공하고 하류 컴포넌트는 API를 사용한다. 고객과 공급자 관계에 있는 두 팀은 상호 협력이 필수적이다. 공급자가 API를 마음대로 변경하면 고객은 변경된 API를 맞추는데 시간을 소모할 수 있고, 반대로 공급자가 무언가를 변경할 때마다 고객으로부터 여러 절차를 거쳐 승낙을 받아야 한다면 공급자는 새로운 개발 시도 자체를 하지 않을 것이다.

 

 

상류컴포넌트를 사용할 때는 상류 서비스의 모델이 자신의 도메인 모델에 영향을 주지 않도록 보호해주는 완충지를 만들어야 한다. 

 

두 바운디드 컨텍스트가 같은 모델을 공유하는 경우도 있다. 두 바운디드 컨텍스트에 같은 모델을 공유함으로써 주문과 관련된 중복 설계를 막을 수 있다. 이런 모델을 공유 커널이라고 부른다.

 

 

 

 

8. 컨텍스트 맵

개별 바운디드 컨텍스트에 매몰되면 전체를 보지 못할 때가 있다. 이를 방지하기 위해 전체 비즈니스를 조망할 수 있는 컨텍스트 맵을 사용한다.

바운디드 컨텍스트 영역에 주요 애그리거트를 함께 표시하면 모델에 대한 관계가 더 명확히 드러난다. 컨텍스트 맵은 시스템의 전체 구조를 보여주기 때문에 하위 도메인과 일치하지 않는 바운디드 컨텍스트를 찾아 도메인에 맞게 바운디드 컨텍스트를 조절하고 사업의 핵심 도메인을 위해 어디에 집중할 지 파악하는 데 도움을 준다.

728x90
반응형

댓글