책: 도메인 주도 설계 핵심
개요
도메인 주도 설계 핵심을 읽고 내용을 정리해본다.
도메인 주도 설계(Domain-Driven Design)와 나
좌절
시작은 좌절이었다. 정확한 시기는 기억나지 않는데 (이 책의 저자인) 반 버논의 도메인 주도 설계 구현(Implementing Domain-Driven Design, 소위 빨간책)으로 DDD에 입문을 시도하였었다. 748쪽의 두꺼운 책을 3권으로 스프링 분철하였는데 그나마 쉽다는 이 책으로도 힘에 부쳐서 중도에 멈춰 버렸다. 그렇게 몇 년간 책장에 꼽혀만 있다가 다시 읽지는 않을 것 같다는 생각에 굳게 마음먹고 버렸다. 이제는 제법 이해가 되니 레퍼런스로 두고 두고 읽을텐데 싶어 아쉽다(주저하고 주저하다 버리면 바로 필요해지는 징크스가 있다).
계기
그러다가 미디엄에서 Mastering DDD: Repository Design Patterns in Go 라는 아티클을 읽었다. 첫 번째 독서가 실패만은 아닌게 DDD의 많은 용어와 개념이 띄엄띄엄 남아있었는데 이 아티클을 읽다보니 그동안 바닥에 가라앉아있던 지식들이 연결될 것만 같은 느낌을 받았다.
새로운 시작
새로이 DDD 관련한 책을 몇 권 읽으며 제법 발효한 DDD 지식을 제대로 정리해보자는 욕심이 생겼고 우선은 이책으로 시작하게 되었다.
TL;DR
이 책 상당히 좋다. DDD의 처음을 이 책으로 시도하는건 추천하지 않지만, 어느정도 전체를 훑어보았다면 이 책으로 DDD의 핵심을 추려보는 것은 전체 그림을 그리는데 많은 도움이 될 것이다.
책 내용 정리
책 전체를 요약하기 보다는 새로운 깨달음, 인상적이었던 부분을 추려보려 한다.
용어에 대한 정의와 설명
책 속에는 DDD 용어들을 명쾌하고 정의하거나 설명하는 문장들이 많은데 이를 여기에 추려둔다.
- DDD: DDD는 높은 가치를 제공하는 소프트웨어를 설계하고 구현하는 데 있어 전략적, 전술적으로 도움을 주는 도구들의 모음이다. p24
- 바운디드 컨텍스트와 보편언어: DDD는 주로 명확하게 바운디드 컨텍스트 내에서 보편언어를 모델링하는 것에 대한 것이다. p38
- 도메인 전문가와 개발자: 도메인 전문가는 비즈니스에 중점을 두는 사람들이다. 반면, 개발자는 소프트웨어 개발에 중점을 둔다. (중략, 요약함) p57
- 서브도메인: 전체 비즈니스 도메인의 하위 부분이다. 하나의 논리적 도메인 모델을 나타내는 것이라고 생각할 수도 있다. p82
- 엔티티: 각 엔티티는 같은 형태를 띠거나 다른 형태의 엔티티들과의 특성을 구별할 수 있는 고유한 식별성을 갖는다. 엔티티는 변할 수 있는 것이며, 여러 번, 아니 항상 그 상태는 계속해서 변할 수 있다. p121
- 값 객체: 엔티티와 달리 고유한 식별성이 없으며, 값 형태로 캡슐화된 속성을 기뵤함으로써 동일함이 결정된다. p122
- 도메인 이벤트: 도메인 이벤트는 바운디드 컨텍스트 내의 비즈니스 관점에서 중요한 사항들에 대한 기록이다. p152
들어가며
이 책의 다이어그램과 그림은 이해에 큰 도움을 주었다. 다만 그림 속 글자들이 너무 작아서 읽기 어려움이 있었다.
이 책이 가장 강조하고 있으며, 여러분들이 가장 중점을 둬야 하는 것은 많은 다이어그램과 그림을 통한 시각적인 학습이다. p21
1장. 나에게 도메인 주도 설계는
스크럼의 가장 중요한 원칙 중 하나는 지식 획득(Knowledge-Acquisition, KA)은 잊어버리고 단순히 백로그를 쌓아두고 하나씩 마구 처리하기만 한다. 비용을 절감하기 위해 소프트웨어 설계를 아예 빼버린다. Book Design: A Practical Introduction 에서 더글라스 마틴은 다음과 같이 설계가 꼭 필요하다 말한다.
설계가 필수적인 것인지, 안 해도 괜찮은지는 요점에서 많이 벗어나 있다. 설계는 필연적이다. 좋은 설계의 대안은 나쁜 설계다. 절대 설계하지 않는 것이 아니다. p30
개발자들은 도메인 전문가와 소통하지 않고, 비즈니스 언어를 주관적으로 해석하여 개발한다. 소통해야 한다.
스크럼에서 지식 획득은 실험과 협업 학습을 통해 이루어지며, “이를 정보 구입(출처: Essential Scrum]”이라고도 한다. p32
2장. 바운디드 컨텍스트 및 보편언어와 전략적 설계
보편언어Ubiquitous Language
보편언어는 1) 팀 구성원들이 소통을 할 때에 사용되고 2) 소프트웨어 모델 안에서 구현한다.
보편언어는 꾸준히 노력해서 유지보수 해야한다.
최고의 학습이나 최고의 지식 획득은 매우 긴 시간에 걸쳐 일어나며, 심지어 “유지”라고 하는 기간에도 일어난다. (중략) 초기에 개발됐던 보편언어는 해가 지나도 계속해서 성장해야 한다. p75
바운디드 컨텍스트Bounded Context
바운디드 컨텍스트는 언어의 경계와 비슷하다. 한국 사람은 한국어권이라는 경계 안에서 한국어로 소통한다. 경계(바운더리)를 넘어서 러시아로 가면 그 언어를 쓸 수 없다.
하나의 바운디드 컨텍스트는 한 팀에 할당해야 한다.
- 하나의 바운디드 컨텍스트를 한 팀에 할당(o)
- 한 팀이 하나 이상의 바운디드 컨텍스트를 담당(o)
- 하나의 바운디드 컨텍스트를 여러 팀이 담당(x)
바운디드 컨텍스트마다 소스 코드, 데이터베이스 스키마도 명확히 분리한다. (정현석. 나아가면 마이크로서비스로 분리가 가능해진다)
바운디드 컨텍스트는 전략적 계획의 핵심이 되는 모든 개념들을 밀접하게 유지하면서 포용해야 하고, 나머지는 모두 제외시켜야 한다. p54
바운디드 컨텍스트를 사용하면 의도치 않은 이점도 생기는데
테스트를 하나의 모델에 집중할 수 있어서 테스트할 양이 적어지고, 더 빨리 테스트를 시행할 수 있다. p54
DDD는 왜 사용하는 걸까?
DDD를 사용하는 이유는 비즈니스 모델의 복잡도가 높기 때문이다. (중략) 프로젝트의 기술적인 측면보다 비즈니스 모델이 더 복잡하기 때문에 DDD를 사용한다. p58
사례를 통한 명세, 또는 행위 주도 개발 Behavior-Driven Development(BDD)
시나리오를 작성하고, 도메인 모델을 구현한 다음에 검증하기 위해 만든 산출물. Given, When, Then으로 표현되는데 주어진 상황에서 어떤 일이 발생하면 어떻게 되어야 하는지를 명세한 것이다. 이는 인수 테스트Acceptance Test 뿐 아니라 단위 테스트Unit Test로도 작성할 수 있다.
기타등등
DDD를 구현하는데 특정 기술을 사용해야는 것이 아니다.
도메인 모델은 기술로부터 최대한 자유로워야 한다. p77
3장. 서브도메인과 전략적 디자인
서브도메인
전체 비즈니스 도메인이 있다면, 그 아래에는 여러 서브도메인이 있다.
- 서브도메인 중 하나는 핵심도메인이다.
- 하나의 바운디드 컨텍스트에는 하나의 서브 도메인이 있다. 하나 이상의 서브도메인이 있다면 최적의 모델이 아닐 수 있다.
- 특정 서브도메인에는 해당 비즈니스를 잘 아는 한 명 이상의 도메인 전문가가 있을 것이다.
DDD를 사용할 때, 바운디드 컨텍스트와 서브 도메인은 일대일(1:1) 관계를 맺어야 한다. p87
4장. 컨텍스트 매핑과 전략적 설계
컨텍스트 매핑은 두 바운더리 컨텍스트 사이에서 일어난다.
서로 다른 두 바운디드 컨텍스트 안에 각각의 보편언어가 있는 것을 생각해보면, 이 선은 두 언어 사이의 통역을 나타낸다. p91
RESTful HTTP
컨텍스트 매핑 방법으로 REST 사용시 주의점
REST를 사용할 때 저지르는 흔한 실수는 도메인 모델 안에 직접적으로 애그리게잇을 반영하는 리소스를 설계하는 것이다. 이렇게 하면 모든 클라이언트에게 준수자 관계를 강요하면서 모델 변화가 리소스의 형태에 영향을 준다. p106
도메인 이벤트Domain Event
도메인 이벤트에 최소한의 정보를 담거나, 모두 담을 수 있다. 혹은 둘을 혼합할 수도 있다.
5장. 애그리게잇과 전술적 설계
애그리게잇
애그리게잇은 트랜잭션과 관련이 있다. 트랜잭션의 동기적인 특성을 생각해보자. 그러려면 밀접해 있어야 한다.
각 애그리게잇은 일관성 있는 트랜잭션 경계를 형성한다. p123
애그리게잇 경험법칙**rule of thumb**
이 규칙을 절대적으로 지켜야 한다는 것이 아니다. 경험법칙이니 가이드 삼아서 따르면 좋은 것이다.
규칙 1: 애그리게잇 경계 내의 비즈니스 불변사항을 보호하라
- 비즈니스 불변사항이란 invariance 를 말하는 것으로 보인다. 절대적으로 지켜져야 하는 사항들을 말한다.
- 예를 들면, 고객의 잔고가 부족하면 제품을 구매할 수 없다 - 와 같은 것이 불변사항인 것이다. 비즈니스에서 요청하는 사항이다.
규칙 2: 작은 애그리게잇을 설계하라
- 애그리게잇은 트랜잭션과 관련이 있다고 했다. 트랜잭션이 커지면 성능에 문제가 된다. 동시성이 떨어지게 되고, 실패하면 롤백할 사항도 많아진다.
- SOLID의 단일 책임 원칙Single Responsibility Principle(SRP)도 염두에 두자. 하나의 목적에 초점을 두면 애그리게잇이 커지지 않는다.
규칙 3: 오직 식별자로만 다른 애그리게잇을 참고하라
이것은 애그리게잇을 작게 유지하고, 동일한 트랜잭션 내에 여러 애그리게잇을 수정하려는 접근을 방지해준다. p131
또한 식별자만을 통해 레퍼런스틀 얻도록 하면, 다양한 형태의 저장 매커니즘으로 쉽게 저장할 수 있다.
규칙 4: 결과적 일관성을 사용해 다른 애그리게잇을 갱신하라
결과적 일관성Eventual Consistency이란 풀어서 말하면 언젠가는 일관성을 만족한다는 것이다. 예를 들어 보면
- 메일을 보냈다 하는데 당장은 메일함에 보이지 않지만 계속 새로 고침을 해보면 언젠가는 메일함에 들어와있다.
- 누군가 나에게 돈을 송금했다면 (요즘 한국에서는 드물지만) 잠시 지나 계좌를 확인해야 입금을 확인할 수 있다.
올바른 크기의 애그리게잇
적당한 크기로 애그리게잇을 설계하는 방법을 소개하한다.
- 애그리게잇 하나에 루트 엔티티 하나만 있는 애그리게잇을 생성한다
- 규칙 2. 작은 애그리게잇을 설계하라 → 일단 가장 작은 크기로 만드는 것이다.
- 애그리게잇 하나를 변경할 때에 함께 변경해야할 다른 애그리게잇의 목록을 작성한다.
- 규칙 1. 애그리게잇 경계 내의 비즈니스 불변사항을 보호하라 → 함께 변경해야 하는 것을 변경해주는 것이 비즈니스 불변사항의 일관성을 만족시켜 준다.
- 함께 변경해야할 애그리게잇들이 언제까지는 변경되어야는지로 분류한다.
- 즉시 변경되어야 하는 것들(sync)과
- 주어진 시간 이내에만 변경하면 되는 것들(async)로 나눌 수 있다.
- 즉시 변경되어야 하는 애그리게잇은 합치는 것을 고려하자.
- 두 개의 엔티티가 바로 변경되어야 한다 → 트랜잭션! → 하나의 애그리게잇!
- 주어진 시간 이내에만 변경하면 된다면 결과적 일관성Eventual Consistency만 만족하면 된다.
- 규칙 4. 결과적 일관성을 사용해 다른 애그리게잇을 갱신하라
- 예를 들면 메시징을 통해 비동기적으로 갱신해주는 것이다.
결국, 의사결정의 기준은 비즈니스이다.
위 단계에서 결정의 기준은 비즈니스이다. 해당 변경, 갱신이 어느 시간내에는 이루어져야 하는지 비즈니스적인 판단에 의해서 결정이 되고 그에 따라 구현되는 것이다.
이런 활동은 결과적 일관성이 기술 주도가 아닌, 비즈니스 주도라는 것을 보여준다. p148
6장. 도메인 이벤트와 전술적 설계
도메인 이벤트는 세심하게 주의해서 붙여주자.
- 도메인 모델의 보편언어를 반영해야 한다.
- 과거에 발생한 것을 서술하기에 과거형 동사로 표현할 수 있다. 예를 들어 ProductCreated, BacklogItemPlanned 등등
7장. 가속화와 관리 도구
비즈니스는 시간이 부족하다. 일정을 줄이려다 보니 설계를 하지 않는다. 그래서 이 장에서는 일정의 제약 속에서 가능한 최고의 설계를 하는 빠른 방법을 소개한다. 이벤트 스토밍이다. 그 외에 인수테스트, 사용자 스토리 매핑 등의 방법도 이야기한다.