본문은 [도메인 주도 개발 시작하기: DDD 핵심 개념 정리부터 구현까지]를 읽고 간단하게 정리한 글입니다. 필요에 따라 생략/수정된 부분이 있을 수 있으며, 내용이 추후 변경될 수 있습니다.
9.1 도메인 모델과 경계
- 한 개의 모델로 모든 하위 도메인을 표현하는 것은 적절하지 않다
- 하위 도메인마다 같은 용어라도 의미가 다르고 같은 대상이라도 지칭하는 용어가 다를 수 있다
- 하위 도메인마다 사용하는 용어가 다르므로 도메인 모델을 개발하려면 하위 도메인마다 모델을 만들어야 한다
- 각 모델은 명시적으로 구분되는 경계를 가져서 섞이지 않도록 한다
- 여러 하위 도메인의 모델이 섞이면 모델의 의미가 약해지고 각 하위 도메인별로 다르게 발전하는 요구사항을 모델에 반영하기 어려워진다
- 모델은 특정한 컨텍스트(문맥) 하에서 완전한 의미를 갖는다
- 같은 제품이라도 카탈로그 컨텍스트와 재고 컨텍스트에서 의미가 다르다
- DDD에서는 바운디드 컨텍스트(Bounded Context)라는 말로써 구분되는 경계를 갖는 컨텍스트를 지칭한다
9.2 바운디드 컨텍스트
- 바운디드 컨텍스트는 모델의 경계를 결정하며 한 개의 바운디드 컨텍스트는 논리적으로 한 개의 모델을 갖는다
- 바운디드 컨텍스트는 용어를 기준으로 구분한다
- 바운디드 컨텍스트는 실제로 사용자에게 기능을 제공하는 물리적 시스템으로 도메인 모델은 바운디드 컨텍스트 안에서 도메인을 구현한다
- 하위 도메인과 바운디드 컨텍스트과 일대일 관계를 맺는 것이 이상적이지만, 실제론 여러 제약에 의해 그렇게 구현하지 못하는 일이 빈번하다
- 여러 하위 도메인을 하나의 바운디드 컨텍스트에서 개발할 때 주의할 점은 하위 도메인이 모델이 섞이지 않도록 하는 것이다
- 한 프로젝트에 각 하위 도메인 모델이 위치하면 전체 하위 도메인을 위한 단일 모델을 만들고 싶은 유혹에 빠지기 쉽다
- 이는 결과적으로 도메인 모델이 개별 하위 도메인을 제대로 반영하지 못해서 하위 도메인별로 기능을 확장하기 어렵게 만드는 원인이 된다
- 하나의 바운디드 컨텍스트가 여러 하위 도메인을 포함하더라도 하위 도메인마다 구분되는 패키지를 구현해야 한다
- 이렇게 함으로써 하위 도메인을 위한 모델이 서로 뒤섞이지 않고 하위 도메인마다 바운디드 컨텍스트를 갖는 효과를 낼 수 있다
- 바운디드 컨텍스트는 도메인 모델을 구분하는 경계가 되므로 바운디드 컨텍스트는 구현하는 하위 도메인에 알맞은 모델을 포함한다
- 위 그림에서 같은 Product라도 바운디드 컨텍스트에 따라 세부 구성이 다르다
9.3 바운디드 컨텍스트 구현
- 바운디드 컨텍스트는 도메인 모델을 포함할 뿐 아니라 도메인 기능을 사용자에게 제공하는 데 필요한 표현 영역, 응용 서비스, 인프라스트럭처 영역을 모두 포함한다
- 도메인 모델의 데이터 구조가 바뀌면 DB 테이블 스키마도 함께 변경해야 하므로 테이블도 바운디드 컨텍스트에 포함된다
- 모든 바운디드 컨텍스트를 반드시 도메인 주도로 개발할 필요는 없다
- 위 그림에서 리뷰는 복잡한 도메인 로직을 갖지 않으므로 단순한 서비스-DAO로 구성된 CRUD 방식을 이용해 구현할 수 있다
- 한 바운디드 컨텍스트에서 두 방식을 혼합해서 사용할 수도 있다
- 대표적인 예가 CQRS 패턴이다
- CQRS는 Command Query Responsibility Segregation의 약자로 상태를 변경하는 명령 기능과 내용을 조회하는 쿼리 기능을 위한 모델을 구분하는 패턴이다
- 각 바운디드 컨텍스트는 서로 다른 구현 기술을 사용할 수도 있다
- 바운디드 컨텍스트가 반드시 사용자에게 보여지는 UI를 가지고 있어야 하는 것은 아니다 (그림[9.8] 참고)
- UI를 처리하는 서버를 두고 UI 서버에서 바운디드 컨텍스트와 통신해서 사용자 요청을 처리하는 방법도 있다 (그림(9.9] 참고)
- 이 구조에서 UI 서버는 각 바운디드 컨텍스트를 위한 파사드(Facade) 역할을 수행한다
9.4 바운디드 컨텍스트 간 통합
- 별도로 구분되어 있던 바운디드 컨텍스트가 시간이 흐름에 따라 하나로 통합될 필요성이 생길 수 있다
- 카탈로그 하위 도메인에 개인화 추천 기능을 도입하게 되면 카탈로그 하위 도메인에 기존에 존재했던 카탈로그 바운디드 컨텍스트와 함께 추천 바운디드가 생긴다
- 이때 통합이 되더라도 각 바운디드 컨텍스트에서 사용하는 도메인 모델은 서로 다르다
- 카탈로그는 제품을 중심으로 도메인 모델을 구현하지만 추천은 추천 연산을 위한 모델을 구현한다
- 다음과 같이 카탈로그의 모델을 기반으로 하는 도메인 서비스를 이용해서 상품 추천 기능을 표현할 수 있다
/**
* 상품 추천 기능을 표현하는 도메인 서비스
*/
public interface ProductRecommendationService {
List<Product> getReccomendationsOf(ProductId id);
}
- 도메인 서비스를 구현한 ProductRecommendationService 클래스는 인프라스트럭처 영역에 위치한다
- 이 클래스는 외부 시스템과의 연동을 처리하고 외부 시스템의 모델과 현재 도메인 모델 간의 변환을 책임진다
- RecSystemClient는 외부 추천 시스템이 제공하는 REST API를 이용해서 특정 상품을 위한 추천 상품 목록을 로딩한다
- 이 REST API가 제공하는 데이터는 추천 시스템의 모델을 기반으로 제공하고 있기 때문에 카탈로그 도메인 모델과 일치하지 않는 데이터를 제공할 것이다
- 따라서 RecSystemClient 내부에서 두 모델 간 변환하는 작업이 필요한데, 이 작업이 복잡하다면 [그림 9.12]와 같이 변환 처리를 위한 별도 클래스를 만들어도 된다
- REST API를 호출해서 두 바운디드 컨텍스트를 직접 통합하는 대신 간접적으로 통합하는 방법도 있다
- 대표적인 간접 통합 방식이 메시지 큐를 사용하는 것이다
- 카탈로그 바운디드 컨텍스트는 사용자 활동 이력을 메시지 큐에 추가한다
- 추천 바운디드 컨텍스트는 큐에서 이력 메시지를 읽어와 추천을 계산하는 데 사용한다
- 각 컨텍스트에서 메시지를 주고 받기 때문에 두 바운디드 컨텍스트가 사용할 메시지의 데이터 구조를 맞춰야 한다
- 메시지 시스템을 카탈로그 측에서 관리하고 있다면 카탈로그 도메인을 따르는 데이터가 담긴다 ([그림9.14] 참고)
- 반대로 추천 측에서 관리하고 있다면 추천 도메인을 따르는 데이터가 담긴다 ([그림9.15] 참고)
- 즉, 어떤 도메인 관점에서 모델을 사용하느냐에 따라 두 바운디드 컨텍스트의 구현 코드가 달라지게 된다
- 두 바운디드 컨텍스트를 개발하는 팀은 메시징 큐에 담을 데이터의 구조를 협의하게 되는데 그 큐를 누가 제공하느냐에 따라 데이터 구조가 결정된다
- 즉, 이 방식은 한쪽에서 메시지를 출판하고 다른 쪽에서 메시지를 구독하는 출판/구독 모델을 따른다
9.5 바운디드 컨텍스트 간 관계
- 바운디드 컨텍스트는 다양한 방식으로 관계를 맺는다
- 가장 흔한 관계는 한쪽에서 API를 제공하고 다른 한쪽에서 그 API를 호출하는 관계이다
- 이 관계에서 API를 사용하는 바운디드 컨텍스트는 API를 제공하는 바운디드 컨텍스트에 의존하게 된다
- 상류(upstream) 컴포넌트는 일종의 서비스 공급자 역할을 하며 하류(downstream) 컴포넌트는 그 서비스를 사용하는 고객 역할을 한다
- 상류 컴포넌트는 보통 하류 컴포넌트가 사용할 수 있는 통신 프로토콜을 정의하고 이를 공개한다
- REST API나 프로토콜 버퍼(Protocol Buffers)와 같은 것이 대표적이다
- 상류 팀의 고객인 하류 팀이 다수 존재하면 상류 팀은 여러 하류 팀의 요구사항을 수용할 수 있는 API를 만들고 이를 서비스 형태로 공개해서 서비스의 일관성을 유지할 수 있는데, 이런 서비스를 공개 호스트 서비스(OPEN HOST SERVICE)라고 한다
- 상류 컴포넌트의 서비스는 상류 바운디드의 도메인 모델을 따른다
- 따라서 하류 컴포넌트는 상류 서비스의 모델이 자신의 모델에 영향을 주지 않도록 보호해 주는 완충 지대를 만들어야 한다
- 위 그림에서 RecSystemClient는 외부 시스템과의 연동을 처리하면서 외부 시스템의 도메인 모델이 내 도메인 모델을 침범하지 않도록 막아주는 역할을 한다
- 즉 내 모델이 깨지는 것을 막아주는 안티코럽션 계층(Anticorruption Layer)이 된다
- 이 계층에서 두 바운디드 컨텍스트 간의 모델 변환을 처리해주므로 다른 바운디드 컨텍스트의 모델에 영향을 받지 않고 내 도메인 모델을 유지할 수 있다
- 두 바운디드 컨텍스트가 같은 모델을 공유할 수 있는데, 이렇게 공유되는 모델을 공유 커널(SHARED KERNEL)이라고 한다
- 공유 커널은 중복을 줄여준다는 장점이 있지만, 한 팀에서 임의로 모델을 변경하면 안 되고 같은 모델을 사용하는 두 팀이 밀접한 관계를 유지해야 한다는 제약이 있다
- 독립 방식(SEPARATE WAY)는 서로 통합하지 않는 방식이다
- 두 바운디드 간에 통합하지 않으므로 서로 독립적으로 모델을 발전시킨다
- 독립 방식에서 두 바운디드 컨텍스트 간의 통합은 수동으로 이루어진다
- 규모가 커질수록 수동 통합에는 한계가 있으므로 두 바운디드 컨텍스트를 통합해야 하는데, 이때 두 바운디드 컨텍스트를 통합해 주는 별도의 시스템을 만들 수도 있다 ([그림 9.21] 참고)
9.6 컨텍스트 맵
- 컨텍스트 맵은 바운디드 컨텍스트 간의 관계를 표시한 것이다
- 컨텍스트 맵을 통해 각 바운디드 컨텍스트의 경계가 명확하게 드러나고 서로 어떤 관계를 맺고 있는지 알 수 있다
- [그림 9.22]는 오픈 호스트 서비스(OHS)와 안티코럽션 계층(ACL)만 표시했는데 하위 도메인이나 조직 구조를 함께 표시하면 전체 관계를 이해하는 데 도움이 된다
- 궁극적으로 컨텍스트 맵으로써 전체 비즈니스를 조망할 수 있다
- 컨텍스트 맵은 시스템의 전체 구조를 보여준다
- 도메인에 맞게 바운디드 컨텍스트를 조절하고 자원을 어떤 바운디드 컨텍스트에 투입할지 파악하는 데 도움을 준다
- 컨텍스트 맵을 그리는 규칙은 따로 없다
- 위와 같이 간단한 도형과 선을 이용해서 각 컨텍스트의 관계를 이해할 수 있는 수준에서 그리면 된다
'책 > 도메인 주도 개발 시작하기: DDD 핵심 개념 정리부터 구현까지' 카테고리의 다른 글
[도메인 주도 개발 시작하기] 11장: CQRS (0) | 2022.11.05 |
---|---|
[도메인 주도 개발 시작하기] 10장: 이벤트 (0) | 2022.11.04 |
[도메인 주도 개발 시작하기] 8장: 애그리거트 트랜잭션 관리 (0) | 2022.10.24 |
[도메인 주도 개발 시작하기] 7장: 도메인 서비스 (0) | 2022.10.21 |
[도메인 주도 개발 시작하기] 6장: 응용 서비스와 표현 영역 (0) | 2022.10.14 |