티스토리 뷰
개요 - 재독
도메인 주도 설계 핵심을 지난 2024년 5월에 읽고 정리해 두었었는데 2024년 9월 다시 읽고 내용을 정리해본다.
재독의 가치가 있는 DDD(Domain-Driven Design)를 잘 요약한 좋은 책이다. 저자 반 버논(Vaughn Vernon)은 그 요약의 어려움을 이렇게 말한다.
장황한 이야기를 60분 동안 이야기할 땐 5분 정도의 준비 시간만 있으면 충분하지만, 5분과 같이 짧은 시간 내에 이야기해야 한다면 몇 시간을 준비해야 하는 어려운 일이기 때문이다. p7
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
DDD 맛보기
전략적 설계
- 바운디드 컨텍스트를 이용하여 도메인 모델을 분리한다. 서브도메인들로 나누어진다.
- 바운디드 컨텍스트 안에서 통하는 보편언어를 개발한다. 보편언어 개발에는 도메인 전문가와 개발자의 긴밀한 소통이 필요하다.
- 바운디드 컨텍스트들 사이는 컨텍스트 매핑을 이용하여 소통한다.
전술적 설계
- 애그리게잇 패턴을 사용한다. 엔티티와 값 객체를 알맞은 크기의 애그리게잇으로 묶는 것이다.
- 도메인 이벤트를 사용하여 도메인을 명확히 모델링하고, 이를 이용하여 정보를 시스템과 공유한다.
2장. 바운디드 컨텍스트 및 보편언어와 전략적 설계
보편언어Ubiquitous Language
보편언어는 1) 팀 구성원들이 소통을 할 때에 사용되고 2) 소프트웨어 모델 안에서 구현한다.
보편언어는 꾸준히 노력해서 유지보수 해야한다.
최고의 학습이나 최고의 지식 획득은 매우 긴 시간에 걸쳐 일어나며, 심지어 “유지”라고 하는 기간에도 일어난다. (중략) 초기에 개발됐던 보편언어는 해가 지나도 계속해서 성장해야 한다. p75
바운디드 컨텍스트Bounded Context
바운디드 컨텍스트는 언어의 경계와 비슷하다. 한국 사람은 한국어권이라는 경계 안에서 한국어로 소통한다. 경계(바운더리)를 넘어서 러시아로 가면 그 언어를 쓸 수 없다.
하나의 바운디드 컨텍스트는
- 한 팀이 담당한다.
- 독립적인 소스 코드 리파지토리가 있어야 한다.
- 데이터베이스 스키마도 명확히 분리한다.
바운디드 컨텍스트를 사용하면 의도치 않은 이점도 생기는데
테스트를 하나의 모델에 집중할 수 있어서 테스트할 양이 적어지고, 더 빨리 테스트를 시행할 수 있다. p54
사례를 통한 명세, 또는 행위 주도 개발 Behavior-Driven Development(BDD)
시나리오를 작성하고, 도메인 모델을 구현한 다음에 검증하기 위해 만든 산출물이다.
Given, When, Then으로 표현되는데 주어진 상황에서 어떤 일이 발생하면 어떻게 되어야 하는지를 명세한 것이다.
이는 인수 테스트Acceptance Test 뿐 아니라 단위 테스트Unit Test로도 작성할 수 있다.
기타등등
DDD는 왜 사용하는 걸까?
DDD를 사용하는 이유는 비즈니스 모델의 복잡도가 높기 때문이다. (중략) 프로젝트의 기술적인 측면보다 비즈니스 모델이 더 복잡하기 때문에 DDD를 사용한다. 이것이 바로 개발자가 도메인 전문가와 함께 비즈니스 모델을 파고들어야 하는 이유다! p58
DDD를 구현하는데 특정 기술을 사용해야는 것이 아니다.
도메인 모델은 기술로부터 최대한 자유로워야 한다. p77
3장. 서브도메인과 전략적 디자인
서브도메인
전체 비즈니스 도메인이 있다면, 그 아래에는 여러 서브도메인이 있다.
- 핵심(core) 도메인: 경쟁자와 차별을 만들어야 하는 영역. 최우선 순위이자 직접 만들어야 한다. 꼭 코드가 아닐 수도 있다.
- 일반(generic) 도메인: 기존 제품을 구매해서 바로 충족시킬 수 있다.
- 지원(supporting) 도메인: 핵심은 아니지만 기존 제품이 없기에 직접 만들어야 한다.
하나의 바운디드 컨텍스트에는 하나의 서브 도메인이 있다. 하나 이상의 서브도메인이 있을 수는 있지만 최적의 모델이 아닐 수 있다.
DDD를 사용할 때, 바운디드 컨텍스트와 서브 도메인은 일대일(1:1) 관계를 맺어야 한다. p87
4장. 컨텍스트 매핑과 전략적 설계
컨텍스트 매핑은 두 바운더리 컨텍스트 사이에서 일어난다.
서로 다른 두 바운디드 컨텍스트 안에 각각의 보편언어가 있는 것을 생각해보면, 이 선은 두 언어 사이의 통역을 나타낸다. p91
컨텍스트 매핑의 종류
- 파트너십: 두 팀의 성공이 긴밀하다. 서로간의 소통을 통해 맞춰나간다ㅏ.
- 공유 커널(Shared Kernel): 두 팀이 공유하는 영역이 있다.
- 고객-공급자: 공급자는 상류(upstream), 고객은 하류(downstream)이다. 서로간의 조율을 할 수 있지만 최종 결정은 공급자이다.
- 준수자(Conformist): 상류에서 하류의 요구사항을 따를 필요가 없다면 하류는 상류의 결정을 준수할 수 밖에 없다.
- 반부패 계층(ACL, Anticurrpution Layer): 하류가 상류를 따르기는 하지만 ACL을 두어서 하류의 모델을 상류에서 독립시켜 준다.
- 공개 호스트 서비스(OHS, Open Host Service): 바운디드 컨텍스트에 접근을 제공하는 프로토콜, 인터페이스를 정의하는 것.
- 공표된 언어(PL, Published Language): 잘 문서화된 정보 교환 언어를 제공하는 것. XML, JSON, Protobuf, Avro 등이 있다. 보통 OHS는 PL을 제공한다.
- 각자의 길(Seperate Ways): 바운디드 컨텍스트 간의 소통, 통합을 포기하고 각자가 알아서 필요한 구현을 한다.
- 큰 진흙 덩어리(Big Ball of Mud): 하나의 바운디드 컨텍스트 안에 뒤섞인 상태의 통합이다.
RESTful HTTP
도메인 모델 자체를 그대로 클라이언트에게 리소스로 제공하면 안된다. 도메인 모델을 클라이언트가 원하는 구성과 형태의 리소스로 만들어 제공해야 한다.
REST를 사용할 때 저지르는 흔한 실수는 도메인 모델 안에 직접적으로 애그리게잇을 반영하는 리소스를 설계하는 것이다. 이렇게 하면 모든 클라이언트에게 준수자 관계를 강요하면서 모델 변화가 리소스의 형태에 영향을 준다. p106
메시징
즉각적인 결과가 필수라면 하나의 애그리게잇 안에 모아두어야겠지만 결과적 일관성(eventual consistency) 만으로 충분하다면 메시징을 사용하자.
메시징을 사용한 통합은 가장 견고한 형태 중 하나인데, (중략) 즉각적인 결과가 필수적이지 않을 때는 메시징을 이용해 좀 더 견고한 시스템을 구축하는 것도 좋은 선택이다. p107
도메인 이벤트(Domain Event)에는 무엇을 담아야 할까?
- 최소한의 정보를 담아서 필요하면 쿼리하게 할 수 있다. 다시 쿼리해야 하니 번거로워 진다.
- 필요하겠다 싶은 것을 꽉꽉 채워담을 수 있다. 트래픽이 무거워지고 보안적인 측면에서 좋지 않을 수 잇다.
- 둘을 적절히 혼합하는 선택도 가능하다.
5장. 애그리게잇과 전술적 설계
애그리게잇
애그리게잇은 트랜잭션과 관련이 있다. 트랜잭션의 동기적인 특성을 생각해보자. 그러려면 밀접해 있어야 한다.
각 애그리게잇은 일관성 있는 트랜잭션 경계를 형성한다. p123
애그리게잇 경험법칙
이 규칙을 절대적으로 지켜야 한다는 것이 아니다. 경험법칙이니 가이드 삼아서 따르면 좋은 것이다.
규칙 1: 애그리게잇 경계 내의 비즈니스 불변사항을 보호하라
- 비즈니스 불변사항(invariance)은 트랜잭션의 ACID중 일관성(consistency)를 말한다. 절대적으로 지켜져야 하는 사항이다.
- 예를 들면, 고객의 잔고가 부족하면 제품을 구매할 수 없다 - 와 같은 비즈니스에서 요청하는 사항이다.
규칙 2: 작은 애그리게잇을 설계하라
- 애그리게잇은 트랜잭션과 관련이 있다. 트랜잭션이 커지면 성능에 문제가 된다. 동시성이 떨어지게 되고, 실패하면 롤백할 사항도 많아진다.
- SOLID의 단일 책임 원칙(SRP, Single Responsibility Principle)도 염두에 두자. 하나의 목적에 초점을 두면 애그리게잇이 커지지 않는다.
규칙 3: 오직 식별자로만 다른 애그리게잇을 참고하라
이것은 애그리게잇을 작게 유지하고, 동일한 트랜잭션 내에 여러 애그리게잇을 수정하려는 접근을 방지해준다. p131
또한 식별자만을 통해 레퍼런스를 얻도록 하면, 다양한 형태의 저장 매커니즘으로 쉽게 저장할 수 있다.
규칙 4: 결과적 일관성을 사용해 다른 애그리게잇을 갱신하라
결과적 일관성Eventual Consistency이란 쉽게 말해 언젠가는 일관성을 만족한다는 것이다. 비즈니스에서 즉각적인 일관성이 필수가 아니라면 사용할 수 있다.
예를 들어 보면
- 메일을 보냈다 하는데 당장은 메일함에 보이지 않지만 계속 새로 고침을 해보면 언젠가는 메일함에 들어와있다.
- 누군가 나에게 돈을 송금했다면 (요즘 한국에서는 드물지만) 잠시 지나 계좌를 확인해야 입금을 확인할 수 있다.
애그리게잇의 크기
적당한 크기로 애그리게잇을 설계하는 방법을 소개한다.
- 애그리게잇 하나에 루트 엔티티 하나만 있는 애그리게잇을 생성한다
- 규칙 2. 작은 애그리게잇을 설계하라 → 일단 가장 작은 크기로 만드는 것이다.
- 애그리게잇 하나를 변경할 때에 함께 변경해야할 다른 애그리게잇의 목록을 작성한다.
- 규칙 1. 애그리게잇 경계 내의 비즈니스 불변사항을 보호하라 → 함께 변경해야 하는 것을 변경해주는 것이 비즈니스 불변사항의 일관성을 만족시켜 준다.
- 함께 변경해야할 애그리게잇들이 언제까지는 변경되어야는지로 분류한다.
- 즉시 변경되어야 하는 것들(sync)과
- 주어진 시간 이내에만 변경하면 되는 것들(async)로 나눌 수 있다.
- 즉시 변경되어야 하는 애그리게잇은 합치는 것을 고려하자.
- 두 개의 엔티티가 바로 변경되어야 한다 → 하나의 트랜잭션안에서 이루어져야 한다 → 하나의 애그리게잇!
- 주어진 시간 이내에만 변경하면 된다면 결과적 일관성(Eventual Consistency)만 만족하면 된다.
- 규칙 4. 결과적 일관성을 사용해 다른 애그리게잇을 갱신하라
- 예를 들면 메시징을 통해 비동기적으로 갱신해주는 것이다.
결국, 의사결정의 기준은 비즈니스이다.
위 단계에서 결정의 기준은 비즈니스이다. 기술적인 의사결정이 아니다.
해당 변경, 갱신이 어느 시간내에는 이루어져야 하는지 비즈니스적인 판단에 의해서 결정이 되고 그에 따라 구현되는 것이다.
이런 활동은 결과적 일관성이 기술 주도가 아닌, 비즈니스 주도라는 것을 보여준다. p148
6장. 도메인 이벤트와 전술적 설계
도메인 이벤트
도메인 이벤트에는 꼭 필요한 만큼의 정보만이 담겨야 한다. 이벤트를 보고 무슨 일이 일어났는지를 알 만큼이 필요한 것이다.
도메인 이벤트가 풍부한 추가 데이터를 담고 있을 수도 있다. (중략) 하지만 도메인 이벤트에 그 의미를 잃을 정도로 너무 만은 데이터를 가득 채우는 일이 없도록 주의해야 한다. p157
발생한 것을 서술하기에 과거형 동사로 표현한다. 예를 들어 ProductCreated, BacklogItemPlanned 등등
명령(command)이 이벤트(domain event)를 발생시킬 수 있고, 특정 시간에 자동으로 발생하는 것일 수도 있다.
명령은 거부할 수 있지만 도메인 이벤트는 이미 발생한 것이기에 논리적으로 부정할 수 없다.
이벤트 소싱
이벤트 소싱은 애그리게잇 인스턴스에 대해 변경된 것이 대한 기록으로, 발생했던 모든 도메인 이벤트를 저장하는 것을 말한다. p162
이벤트 리파지토리는 모든 도메인 이벤트를 추가하는 순차적인 리파지토리 컬렉션 또는 이벤트를 말한다. (중략) 오직 추가만 가능한데, 이런 특성으로 인해 매우 빠르게 동작한다(append-only). p163
7장. 가속화와 관리 도구
설계는 필수적이다.
안타깝게도 이 부정적인 압박에 대한 일반적인 대응은 설계를 하지 않음으로써 일정을 줄이고 시간을 절약하고자 노력하는 것이다. p168
이벤트 스토밍
설계는 필수적인데 일정은 부족하다. 일정의 제약 속에서 가능한 최고의 설계를 하는 빠른 방법으로 이벤트 스토밍을 소개한다.
그 외에 인수테스트, 사용자 스토리 매핑 등의 방법도 이야기한다.
이벤트 스토밍은 빠른 주기의 학습 과정에 도메인 전문가와 개발자 모두가 참여하는 신속한 설계 기술이다.
- 도메인 이벤트(domain event)
- 주황색.
- 발생한 것이기에 과거형 동사를 사용한다. 비즈니스 프로세스를 도출한다.
- 명령(command)
- 밝은 파란색. 이벤트를 생성하는 명령이기에 명령적 문구를 사용한다.
- 따라서 도메인 이벤트의 왼쪽에 위치한다.
- 애그리게잇(aggregate)
- 노란색
- 명령이 실행되어 발생한 도메인 이벤트의 결과가 저장되는 데이터 저장소이다.
- 따라서 명령과 도메인 이벤트의 위쪽에 위치한다.
'book-movie' 카테고리의 다른 글
책: 헬로 Bun (0) | 2024.10.21 |
---|---|
책: 소프트웨어 개발에 ChatGPT 사용하기 (0) | 2024.09.16 |
책: 이처럼 사소한 것들 (0) | 2024.08.08 |
오만과 편견 with ChatGPT (0) | 2024.08.06 |
책: 커맨드라인 러스트 - Rust와 친해지기 (0) | 2024.08.03 |
- Total
- Today
- Yesterday
- 영화
- Shortcut
- Bug
- agile
- go
- websocket
- 독서
- 인텔리제이
- ChatGPT
- notion
- intellij
- 노션
- 클린 애자일
- 체호프
- 독서후기
- OpenAI
- postgres
- bun
- Gin
- JIRA
- github
- API
- 잡학툰
- pool
- 제이펍
- strange
- folklore
- golang
- solid
- 2023
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |