티스토리 뷰
DDD란 무엇일까?
# 참고
DOMAIN DRIVEN DESIGN (도메인 주도 설계 핵심) - 반 버논 지음
# 개요
DDD에 대한 서적을 읽고 DDD가 무엇인지 이해하고 어떻게 코드에 적용시킬 수 있는지 고민해 보려고 한다.
MSA 전환 너무 거대한 시스템을 쪼개는 것이기 때문에 DDD에 대한 기본이 깔려 있어야 한다.
1장. 도메인 주도 설계란?
1-1. DDD가 우리에게 상처를 줄까? 정말 좋은 기술일까?
DDD가 소프트웨어 개발에 대한 복잡한 접근법이라는 이야기 조금 있다. 맞다. DDD는 처음 들어보는 사람들에게는 복잡한 내용이다. 하지만 개발자로서 복잡한 설계를 어떻게 풀어나갈 수 있는지 고민해야 한다. DDD는 이러한 복잡한 소프트웨어 프로젝트에서 사용할 수 이는 수준 높은 기술들을 모은 것이기 때문에 어려울 수 있기는 하나 개발자로서 이해하고 적용하고자 노력할 만한 충분한 가치가 있다.
1-2. 좋은, 나쁜 그리고 효과적인 설계
많은 소프트웨어 개발 팀이 설계를 그다지 중요하게 생각하지 않는다. 또한 스크럼 제품 백로그 처럼 Todo를 작성하여 작성 보드 셔틀을 수행한다. 이러한 백로그를 지식 집약적인 일로 여기고 프로그래머는 소스를 짜기만 하면 된다고 생각한다. 이러한 설계는 엄청난 비즈니스 비용을 쏟아붓는 일이다.
# 나쁜 설계
- 개발자가 너무 기술에만 몰두한 나머지 문제를 신중하게 연구하고 설계하기 보다는 기술적으로 해결하려고 한다.
- 데이터베이스에 너무 큰 우선순위를 부여하고, 대부분의 논의가 데이터베이스 주변 솔루션과 데이터베이스 모델에 집중된다.
- 개발자가 비즈니스 모델에 따라 클래스 이름을 짓는 것에 크게 관심이 없다. 이러한 문제는 협업시 개발자가 명세서를 작성하느라 너무 많은 시간을 보낸다
- 영속성 오페레이션을 비즈니스 로직 중간에 수행하며 문법에 어긋나고 권한이 없어 차단되는 쿼리들 떄문에 사용자가 긴급하게 처리해야 하는 비즈니스 업무를 수행하지 못하도록 방해한다.
- 오퍼레이션을 수행하는 서비스가 또 다른 서비스를 직접 호출하는 강하게 결합된 서비스들이 존재하여 유지보수가 어려울 뿐 아니라 프로세스에 문제를 일으키고 데이터가 불일치 되는 결과로 이어진다.
- 두더지 잡기 처럼 한 가지 문제가 해결돼도 또 다른 문제를 계속 야기시킨다.
- 전반적 지식과 모든 언어를 다룰 수 있는 영웅같은 사람이 있어야 시스템을 관리할 수 있다.
즉, 이러한 문제는 설계를 하지 않고 소프트웨어 설계에만 박차를 가하기 때문에 발생한 문제이다. 설계를 하는 것이 좋은거나 나쁘거나를 판단하는 문제가 아니라 설계는 반드시 수행해야 좋은 코드를 작성할 수 있다. 설계는 단순히 어떤 모양으로 만들지 고민하는 것이 아니다 어떻게 동작하는지 우리 회사가 어떤 강점을 가지고 어떻게 좋은 서비스를 제작할지 고민하는 것이 설계이다.
2장. 바운디드 컨텍스트 및 보편언어와 전략적 설계
2-1. 전략적 설계
전략적 설계는 큰 단위 부터 설계해 나가는 방식으로 비즈니스상 전략적으로 중요한 것, 중요도에 따라 일을 나누는 방법 그리고 필요에 따라 통합하는 최적의 방법을 강조하는 개념이다.
# 바운디드 컨텍스트
# 서브 도메인
서브 도메인이 무엇이고 어떻게 서브도메인이 기존 시스템에 제한되지 않고 복잡성을 다룰수 있는지 설계하는 것이 중요하다.
# 컨텍스트 매핑
여러 개의 바운디드 컨텍스트를통합하는 방법
# 컨텍스트 맵
2개의 바운디드 컨텍스트를 통합하면서 기술적인 메커니즘을 정의하는 것이다.
# 애그리게잇 (Aggregate)
엔티티와 값 객체(VO)를 알맞은 크기의 애그리게잇으로 묶는 애그리게잇 패턴이 중요하다.
2-2. 바운디드 컨텍스트
DDD를 간단한게 정의한다면 바운디드 컨텍스트 내에서 보편언어를 모델링하는 것에 대한 것이다.
# 바운디드 컨텍스트
바운디드 컨텍스트는 동일한 컨텍스트의 범위를 표현한 것이다. 그 범주 내에서 소프트웨어 모델의 각 컴포넌트는 특정한 의미를 갖고, 특정한 일을 수행한다는 의미다. 바운디드 컨텍스트는 모델이 구현되는 곳이고, 바운디드 컨텍스트마다 가각 분리된 소프트웨어 산출물이 나온다. 즉, 바운디드 컨텍스트 안에서 일하는 팀이 생성되고 그 안에서 구성원이 사용하는 언어가 반영된다. 이 언어를 보편언어라고 부른다.
바운디드 컨텍스트는 단일 팀에만 할당되어야 하고, 각 바운디드 컨텍스트마다 독립적인 소스 코드 레파지토리가 있어야 하며 바운디드 컨텍스트 마다 데이터베이스 스키마도 명확히 분리되어야 한다. 한 팀은 다수의 바운디드 컨텍스트를 가질수 있지만 절대 한 바운디드 컨텍스트를 다수의 팀이 가져서는 안된다.
많은 회사에서 작은 도메인 모델로 개발을 시작한다. 하지만 서비스가 점점 커지며 도메인 모델에 개념이 쌓이기 시작하고 언제 멈춰야 하는지 모를 때가 자주 있다. 하지만 여기서 더 많은 개념들이 계속 추가되면 하나의 거대한고, 혼란스럽고, 경계가 제한되지 않은 모델 안에서 너무 많은 개념이 존재하는 것은 물론, 여러 언어가 혼재되면서 점차 모델 내의 언어가 모호해지기 시작한다.
따라서 서로 관련이 없는 다양한 개념들이 수많은 모듈로 확장되거나 오울리지 않는 모듈을 상호 연계시킬 수도 있다. 이렇게 되면 모듈을 수행하는 테스트도 시간이 오래 걸리며 중요한 시점에 이러한 테스트를 건너뛰는 상황이 나타날 수 있다.
따라서 바운디드 컨텍스트는 전략적 계획의 핵심이 되는 모든 개념들을 밀접하게 유지하면서 포용해야 하며 이러한 핵심은 엄격하게 걸러내야 한다.
# 핵심 도메인
바운디드 컨텍스트가 조직의 핵심 전략적 계획으로 개발되고 있을 때, 이를 핵심 도메인이라고 한다. 핵심 도메인은 다른 조직과 경쟁에 대한 차별화를 가지며 너무 많은 것을 핵심 도메인으로 만드려 하면 안된다. 전략적으로 무엇이 핵심이고 무엇을 제외시킬지 현명하게 선택해야 한다. 즉, 최적의 자원과 적절한 투자가 이루어 져야 한다.
# 보편언어 (유비쿼터스 언어)
바운디드 컨텍스트에 따라 팀이 생성되면 그 팀안에서 사용이 되는 언어를 보편언어라고 한다. 보편언어는 팀 모두가 그 표현이 가진 제약사항과 정확한 의미를 이해하고 있어야 한다.
# 컨텍스트 매핑
바운디드 컨텍스트에서 엄격히 걸러져 제외된 다른 모델링 개념들은 어떻게 해야 될까? 제외된 개념들중 대부분은 다른 컨텍스트에 포함될 것이다. 즉, 컨텍스트 매핑을 통해 2개 이상의 바운디드 컨텍스트를 연결하는 것이 중요하다.
# BDD (행위 주도 개발)
BDD는 사례를 통한 명세이다. given-when-tne등의 표현을 사용해 작성한 시나리오는 이전 시나리오 작성 표현들보다 검증에 더 유리하다. BBD 기반 단위 테스트는 실행 가능한 명세와 동일한 목적을 달성시켜준다. 가독성에 대한 문제가 조금있지만 신속하게 시나리오를 검증할 수 있다는 점이 있다.
# 아키텍처
바운디드 컨텍스트는 안에는 무엇이 있을까?
포트와 어댑터 아키텍처 다이어그램을 보면 바운디드 컨텍스트가 도메인 모델 이상의 다양한 요소들로 구서오디는 것을 확이할 수 있다. 이런 레이어들은 거의 모든 바운디드 컨텍스트 내에 존재한다.
컨트롤러, Rest EndPoint, 메세지 리스너, 인풋 어댑터, 유스케이스를 조율하고 트랜잭션을 관리하는 애플리케이션 서비스, 도메인 모델, 영속성 관리나 메시지 발송기, 아웃풋 어댑터등이 있다. 이러한 아키텍처는 도메인 주도 설계 구현(IDDD)에서 자세히 설명해 준다.
- REST EndPoint
- 이벤트 주도 아키텍처
- 커맨드 쿼리 분리 책인 (CQRS)
- 서비스 지향 아키텍처 (SOA)
- MSA
- 반응 메세징 패턴 (Reactive)
# 마이크로서비스와 DDD
마이크로서비스 구축은 DDD의 바운디드 컨텍스트와 본질적으로 유사하다. 사람들은 마이크로서비스가 DDD의 바운디드 컨텍스트보다 훨씬 더 작다고 생각한다. 하지만 마이크로서비스의 정의는 하나의 데이터 형태만 관리한다는 의미이다. 책에서 말하고 싶은 내용은 마이크로서비스와 바운디드 컨텍스트를 같은 논리적인 크기로 생각해도 괜찮다는 것을 말하고 싶은것 같다.
# 기술로부터 자유로운 도메인 모델
기술은 아키텍처 전반에 걸쳐 산재해 있지만, 도메인 모델은 기술로부터 최대한 자유로워야 한다. 예를 들어 트랜잭션은 어플리케이션 서비스에 의해 관리되는 것이지, 도메인 모델에 의해 관리되는 것은 아니다. 즉, 계층에 맞는 역할과 로직을 작성하는게 중요하다는 것을 뜻하는 것 같다.
3장. 서브도메인과 전략적 설계
3-1. 서브도메인이 무엇이고, 문제 영역과 해결영역에서 어떻게 사용하는가?
바운디드 컨텍스트들 중 하나는 핵심 도메인이 될 것이고, 다른 바운디드 컨텍스트에는 다양한 서브도메인이 존재할 것이다. 애자일 프로젝트에서는 하나의 바운디드 컨텍스트와 하나의 서브도메인을 모두 포함한다는 것이다. 하나의 바운디드 컨텍스트 안에 다수의 서브도메인이 있을 수도 있는데, 이 경우 최적의 모델이라고 하기는 그렇다.
# 서브도메인
서브도메인은 전체 비즈니스 도메인의 하위 부분이다. 거대하고 복잡한 프로젝트에서 문제 영역을 이해할 수 있도록 전체 비즈니스 도메인을 논리적으로 쪼개는 데 서브도메인이 사용이 된다.
3-2. 핵심 도메인과 지원 서브도메인, 일반 서브도메인의 차이
서브도메인은 크게 3가지의 유형이 있다.
# 핵심도메인
전략적 투자 영역으로, 주요 자원을 할당하는 명시적인 바운디드 컨텍스트이며 차별화를 만들 영역이기 때문에 높은 우선순위를 갖는다.
# 지원 서브도메인
이미 존재하는 제품으로 해결할 수 없는 맞춤 제작 개발이 필요한 모델링 영역을 말한다. 핵심 도메인과 같이 너무 큰 투자를 하지 않기 위해 아웃소싱을 하거나 직접 개발할 수도 있다.
# 일반 서브도메인
기존 제품 구매를 통해 바로 충족시킬수 있는 경우에 해당한다. 또한 직접 개발해도 상관 없다.
3-3. 레거시 시스템과의 통합을 고려할 때, 서브도메인을 사용할 수 있는 방법
레거시 시스템을 개선하기 위해 서브도메인을 사용할 수도 있다. 레거시 시스템을 개선할 때 논리적 도메인 모델이 존재한다면, 이것을 서브도메인으로 생각해볼 수도 있다.
사진대체 P.86
서브도메인을 활용하면 간단한 다이어그램 안에 표현할 수 있다.
3-4. DDD의 바운디드 컨텍스트와 서브도메인을 일대일 관계로 맺는 것의 중요성
DDD를 사용할 때, 바운디드 컨텍스트와 서브도메인은 일대일 관계를 맺어야 한다. 가능한 하나의 바운디드 컨텍스트 안에 오직 1개의 서브도메인 모델을 두는 것을 목표로 한다.이는 바운디드 컨텍스트를 정확하게 유지시키고 핵심 전략 목표에 집중하는데 도움을 준다.
3-5. 핵심 도메인과 지원 서브도메인을 2개의 서로 다른 바운디드 컨텍스트로 분리하는 것이 현실적이지 않을 때, DDD모듈로 그것을 분리하는 방법
바운디드 컨텍스트 안에 다른 모델을 만들어야 하는 상황이면, 핵심 도메인으로부터 완전히 분리된 형태로 그 모델을 별도의 영역에 정의해야 한다.
4장. 컨텍스트 매핑과 전략적 설계
4-1. 컨텍스트 매핑의 다양한 종류
애자일 프로젝트 관리 핵심 도메인을 다른 바운디드 컨텍스트와 통합해야 한다고 하였다. DDD에서는 이런 통합을 컨텍스트 매핑이라고 한다. 서로 다른 바운디드 컨텍스트 사이에는 통합뿐만 아니라 팀 간의 다양한 관계도 존재할 수 있다.
#매핑의 종류
# 파트너쉽
두 팀은 일련의 목표에 대한 의존성을 맞추기 위해 파트너쉽을 구성한다. 즉, 두 팀이 함께 성공하거나 다같이 실패한다는 의미이다. 두 팀은 서로 밀접한 관련이 있기 때문에 상호간 의존적인 작업이나 여러 일정들을 조율하고, 통합을 적절하게 유지하기 위해 지속적으로 노력해야 한다. 파트너십이 상호간 이점을 제공한다면 관계를 지속시키고, 이점이 사라지는 상황이라면 다른 관계로 매핑을 설정해야 한다.
# 공유커널
고유커널은 2개팀 사이에 작지만 공통인 모델을 공유하는 관계를 나타낸다. 공유하는 모델의 코드, 빌드를 관리하고 테스트하는 것은 한 팀에서 맡아 수행하여야 한다. 또한 열린 의사소통이 가능해야 하고 지속적인 합의가 있어야 하기 때문에 먼저 처리하기도 어렵고, 관리하는 것도 쉽지 않다. 하지만 각자 저마다의 길을 가는것 보다 공유 커널이 더 좋은 결과를 얻을 수도 있다.
# 고객-공급자
고객-공급자는 2개의 바운디드 컨텍스트가 상하관계를 가지는 것이다. 공급자는 상류, 고객은 하류로 공급자는 고객이 원하는 것을 제공해야 하기 때문에 관계를 주도하는 것은 공급자이다.
# 준수자
준수자 관계는 상류와 하류 팀이 있고, 상류 팀이 하류 팀의 특정 요구에 지원할 동기가 없는 경우에 나타난다. 하류 팀이 자기들의 특정 요구에 맞춰 상류 모델의 보편언어를 계속 변환시키는 것은 쉽지 않기 때문에 현재의 상류팀 모델을 그대로 따른다. 예를 들어 아마존과 제휴하는 판매자는 아마존과 시스템을 통합하려고 할때 아마존 모델을 준수하는 것처럼 확실하게 자리잡은 거대하고 복잡한 모델과 통합할 때 이러한 유형이 나타난다.
# 반부패 계층
반부패 계층은 가장 방어적인 컨텍스트 매핑 관계로 하류팀의 보편언어와 상류팀의 보편언어 사이에 번역 계층을 만드는 것이다. 상류 모델로부터 하류 모델을 독립시켜 둘 사이를 번역하는 계층을 두면 외부의 이질적인 개념으로부터 독립성을 유지시킬 수도 있다. 하지만 이러한 패턴은 특정상황에서 비용이 매우 높아질 수 있다.
# 공개 호스트 서비스
공개 호스트 서비스는 바운디드 컨텍스트에 대한 접근을 제공하는 프로토콜이나 인터페이스를 정의한다. 프로토콜은 바운디드 컨텍스트와 통합하고자 하는 모두가 용이하게 사용할 수 있도록 공개되어 있어야 한다. API로 제공하는 서비스는 문서화가 잘돼 있고 사용하기가 좋다. 반부패 계층을 만들 시간이 없다 해도 이 모델을 사용하는 것이 레거시 시스템들을 맞닥 뜨리는 것보다 낫다.
# 공표된 언어
바운디드 컨텍스트의 규모에 관계없이 모두 간단히 사용과 번역을 가능하게 하는 잘 문서화된 정보 교환 언어이다. 공표된 언어는 대표적으로 XML, JSON이 있다.
4-2. 컨텍스트 매핑 통합 방법
(RPC, REST API, 메시징)
바운디드 컨텍스트 매핑을 통합하는 방법은 크게 3가지 정도가 있다. 3가지를 소개하기 이전에 통합하는 과정에서 데이터베이스의 통합은 절대 피해야 하며 통합을 강요 받는 다면 반부패 계층을 사용해서 모델을 독립시켜야 한다.
# SOAP을 이용한 RPC
사진 P.102
원격 프로시저 호출인 RPC는 다양한 방법으로 동작한다. RPC의 잘 알려진 사용법 중 하나는 SOAP을 이용하는 방법이다. SOAP을 이용한 RPC는 다른 시스템이 서비스를 사용할 때 마치 단순히 로컬 프로시저나 메서드를 호출하는 것처럼 사용하는 개념이다.
SOAP은 네트워크상에서 요청, 원격 시스템에 요청 전달, 성공적 수행, 네트워크를 통한 결과 반환을보장해야 한다. 또한 첫 통합 수행 시점에 예기치 못한 네트워크 장애나 지연을 동반할 수 있다. 또한 SOAP를 이용하는 RPC는 바운디드 컨텍스트 사이의 강한 결합을 암시한다. 더불어 RPC의 주된 문제는 견고함이 떨어져 호스팅하는 시스템에 문제가 생기면, 간단한 프로시저 호출은 에러 결과만 받은 채 완전히 실패한다. 따라서 사용하기 쉬워 보인다고 해서 속으면 안 된다. 하지만 RPC는 제대로 동작하기만 한다면, 통합하기 매우 좋은 방법일 수 있다.
# Restful HTTP
Restful HTTP는 네트워크나 서비스 제공자의 장애 또는 예기치 않은 지연 등 RPC에서의 실패 원인과 동일한 사유로 실패할 수도 있다. 하지만 Restful HTTP는 인터넷의 전제에 기반을 두고 웹이 보여준 신뢰성, 확장성 그리고 지금까지의 성공에 대해 누가 흠 잡을 수 있는 상황은 아니다.
REST를 사용할 때 저지르는 흔한 실수는 도메인 모델 안에 직접적으로 어그리게잇을 반영하는 리소스를 설계하는 것이다. 이렇게 하면 모든 클라이언트에게 준수자 관계를 강요하면서 모델 변화가 리소스의 형태에 영향을 준다. 따라서 리소스가 클라이언트 주도의 유스케이스를 지원할 수 있도록 종합적으로 설계해야 한다. 도메인 모델이 아니라 클라이언트에게 제공하는 리소스가 그들이 원하는 것에 대한 구성과 형태를 갖도록 고려해야 한다는 의미이다.
# 메시징
메세징을 사용하는 통합은 가장 견고한 형태 중 하나이다. RPC와 REST와 달리 분절된 형태와 일시적인 결합을 대부분 제거할 수 있기 때문이다. 메시지 교환에서는 지연이 발생할 수 있기 때문에 즉각적인 결과를 필요로 하지 않는 다면 메세징을 이용해 좀 더 견고한 시스템을 구축하는 것이 좋다. 동기화로 처리하게 된다면 다른 클라이언트가 요청한 것이 막혔을 때 다른 모든 요청이 막혀질 수도 있기 때문이다.
일반적으로 바운디드 컨텍스트 내의 어그리게잇은 도메인 이벤트를 만들때 관심 있는 다른 컨텍스트들은 발생되 이벤트를 사용한다. 즉, 바운디드 컨텍스트가 도메인 이벤트를 받으면, 이벤트의 형태와 값을 토대로 동작을 수행한다.
통합에 메시징을 사용하는 경우, 메시징 메커니즘의 품질에크게 좌우된다. 메시징 매커니즘은 적어도 한번의 전달을 통해 모든 메시지 수신을 보장해야 한다. 적어도 한 번이라는 말은 메시지 소실, 느린 반응, 수신자 장애등 장애 상황에서 주기적으로 메시지를 재전달 하는 패턴이다. 즉, 발송자는 단 한 번 메시지를 전달했더라도 수신자에게 메시지는 여러번 전달될 수 있다. 또한 오퍼레이션이 여러 번 수신되더라도 동일한 결과를 반환하거나 안전하게 처리되어야 한다.
# REST를 사용해 비동기하기
점차 증가하는 리소스들에 대해 REST 기반으로 폴링하는 방식으로 비동기 메시징을 구현해 처리할 수도 있다. 서비스에서 계속 이벤트가 발생하는 상환이라면 서비스와 클라이언트 사이에 비동기 오퍼레이션을 유지하는 것은 매우 안정적인 방식이다. 서비스가 갑자기 이용이 불가능하게 되면 클라이언트는 특정한 주기로 재시도할 수 있따. (추후 IDDD)
5장. 애그리게잇과 전술적 설계
# 엔티티 (Entity)
엔티티는 독립적인 것이다. 다른 엔티티들과 구별할 수 있는 고유한 식별성을 갖는다.
# 값 객체 (VO)
값 객체는 엔티티와 달리 고유한 식별성이 없으며, 값 형태로 캡슐화된 속성을 비교함으로써 동일함이 결정된다. 또한 값 객체가 어떤 것을 나타내기 보다는 엔티티를 서술하는데 사용된다.
5-1. 어그리게잇 패턴이 무엇이고, 왜 사용해야 하는가?
# 애그리게잇 이란?
애그리게잇은 1개 이상의 엔티티로 구성되고, 그중 한 엔티티는 애그리게잇 루트라고 부른다. 애그리게잇은 값 객체와 엔티티를 포함할 수 있고 포괄하는 상위 개념이다. 여기서 어그리게잇 루트는 다른 모든 요소를 개념적으로 표한할 수 있는 명칭이다.
5-2. 일관성 경계를 염두에 두고 설계하는 것의 중요성
애그리게잇은 일관성있는 트랜잭션 경계를 형성한다. 트랜잭션 제어가 데이터베이스에 커밋될 때, 한 애그리게잇 내의 모든 구성 요소는 반드시 비즈니스 규칙을 따르면서 일관성 있게 처리된다. 또한 애그리게잇은 트랜잭션 이후 일관성이 지켜질 필요가 없는 다른 요소를 포함해서는 안 된다는 뜻은 아니다. 객체들 전체에 대한 데이터 처리 원칙을 유지하면서 제어하는 트랜잭션으로 분리되어야 한다는 뜻이다.
트랜잭션에 경계를 두는 이유는 비즈니스 때문인데, 애그리게잇이 유효한 상태인지, 아닌지를 결정하는 것은 비즈니스와 관련돼 있는 일이기 때문이다. 애그리게잇이 완전하고 유효한 상태로 저장되지 않는다면, 수행된 비즈니스 오퍼레이션은 비즈니스 규칙에 어긋난 것으로 간주해야 한다.
2개의 어그리게잇이 정의돼 있는데, 둘 중 하나만 단일 트랜잭션으로 커밋돼야 한다. 즉, 하나의 트랜잭션에는 오직 1개의 어그리게이만을 수정하고 커밋하는 것이 좋다. 다른 애그리게잇은 별도 분리된 트랜잭션으로 수정, 커밋된다. 때문에 애그리게잇은 트랜잭션의 일관성을 만드는 경계라고 부르는 이유이다. 트랜잭션의 일관성과 성공을 보장하도록 애그리게잇 구성요소들을 설계하는 것이 중요하다.
5-3. 효과적인 애그리게잇 설계의 4가지 규칙
1. 애그리게잇 경계 내에서 비즈니스 불변사항들을 보호하라.
규칙1은 트랜잭션이 커밋될 때 비즈니스의 일관성이 지켜지는 것에 기반을 두고 애그리게잇 구성 요소를 결정해야 한다는 의미이다.
2. 작은 애그리게잇을 설계하라.
규칙2는 각 애그리게잇의 메모리 사용량과 트랜잭션 범위가 비교적 작아야 함을 강조하는 것이다. 애그리게잇이 작을 수록 빠르게 로드되고, 더 작은 메모리를 차지하며, 가비지 컬렉터도 더 빠르다. 하지만 가장 중요한 것은 이 애그리게잇들은 이전의 큰 클러스터의 애그리게잇보다 훨씬 더 자주, 성공적인 트랜잭션을 수행할 것이라는 점이다. 이 규칙을 따르면 한 명의 개발자가 관리할 수 있을 만큼 작기 때문에 각 애그리게이시 좀 더 쉬워지는 부가적인 이득을 얻을 수 있으며, 테스트 또한 쉬워진다. 애그리게잇을 설계할 때는 SRP(단일 책임의 원칙)을 따르는 것이 좋다. 애그리게잇이 너무 많은 일을 하면 다시 크기를 나누는 것에 재논의할 가능성이 크다.
3. 오직 ID를 통해 다른 애그리게잇을 참고하라
규칙3의 핵심은 오직 식별자로만 다른 애그리게잇을 참고하라는 것이다. 이렇게 하면 애그리게잇을 작게 유지하고, 동일한 트랜잭션 내에 여러 애그리게잇을 수정하려는 접근을 방지해준다. 이 규칙은 더 적은 메모리 요구와 리파지토리로부터의 빠른 로딩을 통해 애그리게잇 설계를 작고 효율적으로 유지할 수 있게 해준다. 또한 동일한 트랜잭션 내에 다른 애그리게잇을 수정하지 않는 규칙이 잘 지켜지도록 해준다. 식별자만을 통해 얻을수 있는 이점은 애그리게잇을 DB, 저장매커니즘 쉽게 저장할 수 있으며 JSON기반의 리파지토리 사용도 쉽게 결정할 수 있다.
4. 결과적 일관성을 사용해 다른 애그리게잇을 갱신하라.
도메인 이벤트는 애그리게잇에 의해 발행되고, 이에 관심이 있는 바운디드 컨텍스트는 이를 전달받는다. 이처럼 메시징 메커니즘은 구독을 통해 관심 있는 파티들에게 도메인 이벤트를 전달한다. 관심있는 바운디드 컨텍스트는 도메인 이벤트를 발행한 컨텍스트일 수도 있고, 다른 바운디드 컨텍스트일 수도 있다. 트랜잭션 경계에 따라 애그리게잇을 분리켜야 초기 단계부터 거대한 처리를 만들지 않도록 해준다. 따라서 애그리게잇을 사용하면 결과적으로 트랜잭션의 실패 경험을 줄여줄 수는 있다.
5-4. 빈약한 도메인 모델
애그리게잇 구현에 대한 작업을 할 때, 빈약한 도메인 모델(Anemic Domain Model)을 만날 수 있다. 이것은 객체지향 도메인 모델을 사용하면서, 모든 애그리게잇이 비즈니스 행위가 아닌 읽고(getter), 쓰는(setter) 공객 접근만 가지는 것이다. 이런 모델링은 기술적이 부분에 초점을 맞추다 보니 발생하는 경향이 있고 모든 오버헤드를 떠안아버리는 상황을 만든다.
또한 비즈니스 로직이 도메인 모델을 넘어 애플리케이션 서비스까지 새어 나가지 않도록 주의해야 한다. 비즈니스 로직을 도메인 모델 안에 정의하지 못한다면, 빈약한 도메인 모델이 만들어낸 버그들 때문에 고생할 수밖에 없다.
5-5. 애그리게잇의 행위를 모델링하는 방법
본질적인 소석을 위해 읽기 접근자(getter)와 같은 간단한 해위를 추가해도 된다. 하지만 쓰기 접근자(setter)를 공개적으로 드러내고 싶지 않다면, 어떻게 공개 속성 쓰기 접근자 없이 속성 값을 변경할 수 있을까? 객체지향 언어들을 사용하고 있다면, 내부 상태를 변경시키는 행위와 관련된 메서드들을 사용해서 값을 변경하면 된다.
만일 속성 쓰기 메서드를 공개해 버린다면 엔티티의 값 설정을 위한 로직이 모델 밖에 구현될 것이기 때문에 빈약한 도메인으로 쉽게 빠질 수 있다. 따라서 속성 쓰기 메서드를 공개 시킬것인지를 잘 생각해 보아라
** 좀 더 쉬운말로 접근지정자를 public으로 하는 것을 고민해라... 인듯?
5-6. 설계에 적절한 추상화 수준을 선택하는 것의 중요성
효과적인 소프트웨어 모델은 항상 일을 하는 비즈니스의 방식을 고려한 일련의 추상화에 기반을 두고 있다. 이떄 모델링하는 각 개념마다 적절한 수준의 추상화를 선택해야 한다. 하지만 개발자가 가끔 너무 지나칠 정도로 추상화를 적용하기도 한다. 너무 추상화하려 하면 다음과 같은 문제가 발생한다.
- 소프트웨어 모델의 언어가 도메인 전문가의 멘탈 모델과 일치하지 않는다.
- 추상화 수준이 너무 높아서 각 개별적인 형태의 세부 사항을 모델링하기 시작하면 어려운 상황에 빠질 것이다.
- 복잡한 계층 구조를 만든다.
- 중요한 문제를 해결하려다 필요한 것보다 훨씬 많은 코드를 생산할 것이다.
- 잘못되 추상화 수준은 사용자 인터페이스까지 영향을 미친다.
5-7. 올바른 크기의 애그리게잇 구성 기술과 테스트 가능하도록 설계하는 방법
비즈니스 불변사항을 보호할 일관성 경계는 여전히 유지하면서도, 애그리게잇의 경계를 결정하고, 큰 클러스터를 설계하는 것을 방지하는 것이 중요하다. 이미 큰 클러스터의 어그리게잇을 만들었다면, 좀 더 작은것들로 리팩토링하는데 이 방법을 사용할 수도 있다. 또한 일관성을 유지하기 위해 기술로 주도하는 것이 아닌 비즈니스로 주도해야 한다. 다수의 애그리게잇 사이에 갱신을 위한 기술적인 방안도 찾아야 한다. 다양한 엔티티 간에 발생하는 갱신의 수용 가능한 소요 시간을 결정할 수 있는 것은 오직 비즈니스다. 또한 적절한 트랜잭션으로 처리돼야 하는 것들은 동일한 애그리게잇으로 관리해야 한다는 의미이다. 또한 결과적인 일관성이 필요한 경우에는 메시징과 같은 도메인 이벤트를 통해 관리해야 한다는 의미다.
6장. 도메인 이벤트와 전술적 설계
도메인 이벤트는 바운디드 컨텍스트 내의 비즈니스 관점에서 중요한 사항들에 대한 기록이다. 도메인 이벤트가 전략적 설계를 위해 매운 중요한 도구인 것을 계속 말해 왔다. 또한 전술적 설계를 하는 동안 도메인 이벤트의 개념이 정립되면서 핵심 도메인의 일부가 된다.
특정한 오퍼레이션이 다른 애그리게잇에서 명확하게 발생하기 전에는 한 애그리게잇이 생성되거나 수정될 수 없다.
1. 지갑잃어버렸어
2. 큰일이야
3. 찾았어
4. 잘됐다.
이 순서가 바뀌면 안되는 것이다. 즉 인과관계가 적절하게 맺어져 있지 않다면, 전체 도메인 상황이 잘못되거나 혼란스러워진다. 도메인 이벤트가 만들어지면 바운디드 컨텍스트와 다른 자원들은 이벤트를 받아 활용한다. 이벤트에 관심이 있는 이벤트 리스너들에게 관련 상황의 발생을 알리는 매우 강력한 방법이다.
6-1. 도메인 이벤트를 설계, 구현, 사용하기
# 도메인 이벤트 이름을 세심하게 붙여라
도메인 이벤트의 이름은 보편언어를 반영해야 하며 도메인 모델 안에서 발생하는 사건과 모델 밖을 이어주는 다리를 형성할 것이다. 이런 다리들은 이벤트를 통해 모델들이 원활히 의사소통하는데 필수적인 역할을 한다.
# 이벤트 소싱
이벤트 소싱은 애그리게잇 인스턴스에 대해 변경된 것에 대한 기록으로, 발생했던 모든 도메인 이벤트를 저장하는 것을 말한다. 즉, 애그리게잇 상태 전체를 저장하는 대신, 발생했던 각 도메인 이벤트 모두를 저장한다.
하나의 애그리게잇 인스턴스에 발생했던 모든 도메인 이벤트를 발생한 순서대로 이벤트 스트림에 구성한다. 이벤트 스트림은 애그리게잇에 가장 처음 발생했던 도메인 이벤트로 시작해서 마지막 도메인 이벤트까지 계속된다. 애그리게잇 인스턴스에 새로운 도메인 이벤트가 발생하면, 이벤트 스트림의 마지막에 추가한다. 이 과정과는 반대로, 에그리게잇에 이벤트 스트림을 재적용하면 저장된 저옵가 메모리로 환원된다. 즉, 이벤트 소싱을 사용하면 어떤 사유로 인해 메모리가에서 삭제됐던 애그리게잇들을 이벤트 스트림을 통해 온전히 환원시킬 수 있다. 이벤트 소싱을 사용함으로써 얻을 수 있는 가장 큰 이점 중 하나는 핵심 도메인에서 계속 발생하는 모든 기록을 개별적인 발생 수준으로 저장한다는 점이다.
'DDD' 카테고리의 다른 글
5장. 엔티티 / IDDD (도메인 주도 설계 구현) (0) | 2022.03.14 |
---|---|
4장. 아키텍처 / IDDD (도메인 주도 설계 구현) (0) | 2022.03.02 |
3장. 컨텍스트 맵 / IDDD (도메인 주도 설계 구현) (0) | 2022.02.25 |
2장. 도메인, 서브도메인, 바운디드 컨텍스트 / IDDD (도메인 주도 설계 구현) (0) | 2022.02.20 |
1장. DDD를 시작하며 / IDDD (도메인 주도 설계 구현) (0) | 2022.02.19 |