목차10개 항목
출처와 참고
ByteByteGo의 System Design 101 가이드 「8 Key Concepts in Domain-Driven Design」, 「Key Terms in Domain-Driven Design」을 바탕으로 재구성한 노트다. 원 개념은 에릭 에반스의 책 『Domain-Driven Design』(2003)에서 나왔다. 데이터 중심 설계에서 넘어가는 부분은 「Database Driven Development에서 진짜 DDD로의 선회」, 「필요한 내용만 추려서 DDD 당장 써먹기」를 바탕으로 보강했다.
DDD란
도메인 주도 설계는 소프트웨어 설계를 비즈니스 도메인의 모델에서 출발시키는 방법론이다. 데이터베이스 테이블이나 화면이 아니라 "이 업무가 어떻게 돌아가는가"를 먼저 모델로 만들고, 그 모델이 DB 스키마, API, 코드 구조를 이끈다.
복잡한 업무 규칙이 많은 시스템에서 효과가 크다. 단순한 CRUD 애플리케이션에는 과하다.
전략적 설계: 모델의 경계
| 개념 | 뜻 |
|---|---|
| 유비쿼터스 언어(Ubiquitous Language) | 개발자와 업무 전문가가 같은 단어를 같은 뜻으로 쓴다. 회의, 문서, 코드의 클래스·메서드 이름이 모두 이 언어를 따른다 |
| 바운디드 컨텍스트(Bounded Context) | 한 모델이 일관된 의미를 갖는 경계. 같은 "상품"이라도 카탈로그 컨텍스트와 배송 컨텍스트에서는 다른 모델이다 |
| 컨텍스트 맵 | 바운디드 컨텍스트 사이의 관계(누가 누구의 모델에 맞추나, 번역 계층을 두나)를 그린 지도 |
바운디드 컨텍스트는 느슨한 경계로 업무 사이의 연관을 표현한다. 마이크로서비스의 서비스 경계를 정할 때 바운디드 컨텍스트를 기준으로 삼는 경우가 많다(마이크로서비스 아키텍처).
전술적 설계: 모델의 구성 요소
도메인 객체
| 구성 요소 | 정의 | 예 |
|---|---|---|
| 엔티티(Entity) | 고유 ID와 생애 주기가 있는 객체. 속성이 바뀌어도 같은 ID면 같은 것이다 | 주문, 회원 |
| 값 객체(Value Object) | ID 없이 값으로만 구별되는 객체. 엔티티의 속성을 묘사하고, 불변으로 다룬다 | 금액(수량 + 통화), 주소, 기간 |
| 애그리거트(Aggregate) | 함께 변경되어야 하는 엔티티와 값 객체의 묶음. 변경과 저장의 단위다 | 주문 + 주문 항목들 + 배송지 |
| 애그리거트 루트 | 애그리거트의 대표 엔티티. 바깥에서는 루트를 통해서만 내부를 바꾼다 | 주문 |
flowchart TB
ext["바깥"] -->|"이것만 참조"| root
subgraph agg["주문 애그리거트"]
root["Order (루트, id=42)"] --> l1["OrderLine (엔티티)"]
root --> l2["OrderLine (엔티티)"]
root --> addr["ShippingAddress (값 객체)"]
end불변식(총액 = 항목 합)은 루트가 지킨다
값 객체와 DTO의 차이는 DTO, VO, DAO, Repository에 있다.
생애 주기를 다루는 객체
| 구성 요소 | 역할 |
|---|---|
| 리포지토리(Repository) | 애그리거트를 저장하고 불러온다. 도메인 쪽에서는 컬렉션처럼 보이고, 저장 기술은 숨긴다 |
| 팩토리(Factory) | 애그리거트를 만드는 복잡한 과정을 맡는다. 만들어진 순간부터 불변식을 지키게 한다 |
행위를 다루는 객체
| 구성 요소 | 역할 |
|---|---|
| 도메인 서비스(Domain Service) | 한 엔티티에 넣기 어색한, 여러 애그리거트를 조율하는 업무 로직 |
| 도메인 이벤트(Domain Event) | 애그리거트에 일어난 일을 과거형으로 기록한 것("주문이 결제됨"). 발행하면 다른 컨텍스트가 구독해 반응하거나 상태를 다시 구성한다 |
계층 구조
DDD는 복잡도를 계층으로 나눠 관리한다. 네트워크 계층처럼 각 층이 아래층에만 기댄다.
| 계층 | 담는 것 |
|---|---|
| 사용자 인터페이스 | 화면, API 컨트롤러 |
| 애플리케이션 | 유스케이스 흐름 조율, 트랜잭션. 업무 규칙은 없다 |
| 도메인 | 엔티티, 값 객체, 애그리거트, 도메인 서비스. 업무 규칙이 여기 산다 |
| 인프라스트럭처 | DB 접근, 메시징, 외부 API 구현 |
도메인 계층이 인프라를 모르게 하려고 의존을 뒤집으면 헥사고날 아키텍처가 된다.
모델을 찾는 법
업무 지식에서 모델을 뽑아내는 기법이 여럿 있다. 대표적인 것이 이벤트 스토밍(Event Storming)이다. 업무 전문가와 개발자가 함께 "일어나는 일(도메인 이벤트)"을 시간순으로 붙여 나가고, 그 이벤트를 일으키는 명령과 애그리거트, 경계를 찾아낸다.
데이터 중심 설계에서 넘어갈 때
많은 시스템은 테이블부터 그리고 그 위에 화면과 로직을 얹는 데이터베이스 주도 개발로 시작한다. 이 방식은 「데이터를 넣는 것」을 목표로 착각하게 만들고, 모든 것을 촘촘히 연결한 닫힌 스키마는 나중의 선택지를 막는다. DDD로 옮겨 갈 때의 실천은 다음과 같다.
| 실천 | 내용 |
|---|---|
| 도메인을 먼저, 인프라는 나중에 | 기술 스택을 성급히 정하지 않는다. 테스트로 검증하며 도메인 계층을 먼저 만들고, 안정되면 필요한 저장소·메시징을 고른다 |
| 이름을 계속 다듬는다 | 현장 사용자가 클래스·메서드 이름을 듣고 이해하는지가 기준이다. 어색한 이름은 도메인 이해가 부족하다는 신호다 |
| 서비스를 빈약하게 두지 않는다 | DAO만 호출하는 서비스는 계층만 늘린다. 업무 규칙은 엔티티와 도메인 서비스에 둔다. 엔티티가 비대해지면 값 객체와 도메인 서비스로 책임을 나눈다 |
| 경계를 넘는 데이터는 값으로 | 서비스 사이에는 특정 시점의 스냅샷(값 객체)이나 도메인 이벤트를 보낸다. 바뀌지 않는 값이라 재전송·감시 부담이 줄어든다 |
| 프레임워크보다 개념 | 애너테이션 사용법만 익히면 한계에 부딪힌다. 개념을 알아야 기술의 이점과 한계를 판단하고 다른 기술로 옮긴다 |
효과가 큰 곳은 데이터가 여러 곳에 흩어지는 시스템(복제 DB, 마이크로서비스)이다. 정합성을 누가 책임지는지가 설계의 핵심이 되기 때문이다. 반대로 DB 하나에 정합성을 저장 프로시저로 맞추는 시스템이라면 DDD의 이득이 작다.
정리
- DDD는 업무 도메인의 모델을 설계의 출발점으로 삼는다. 복잡한 업무 규칙이 있을 때 쓴다.
- 유비쿼터스 언어로 말을 맞추고, 바운디드 컨텍스트로 모델이 유효한 경계를 정한다.
- 엔티티는 ID로, 값 객체는 값으로 구별한다. 애그리거트는 변경과 저장의 단위이고 루트를 통해서만 바꾼다.
- 리포지토리·팩토리가 생애 주기를, 도메인 서비스·도메인 이벤트가 행위를 맡는다.
관련 노트: 소프트웨어 아키텍처 패턴, 마이크로서비스 아키텍처, DTO, VO, DAO, Repository, 객체 지향의 본질: 메시징, 책임 주도 설계