Domain-Driven Design
DDD 는 에릭 에반스 (Eric Evans) 가 2003 년 제안한 방법론으로, Domain-Driven Design(DDD) 는 소프트웨어 개발에서 도메인 (비즈니스 영역) 의 복잡성을 효과적으로 해결하기 위해 도메인 모델링을 중심에 두는 접근법이다.
전략적 설계에서는 시스템을 Bounded Context, Subdomain, Context Map으로 구획화하며, 전술적 설계에서는 Entity, Value Object, Aggregate, Repository, Domain Event, Factory, Service 구조를 코드에 구현한다. 핵심은 도메인 전문가와 개발자가 유비쿼터스 언어를 통해 소통하며, 바운디드 컨텍스트별로 명확한 모델을 구축하고, 엔티티, 값 객체, 애그리게이트, 도메인 서비스, 도메인 이벤트 등 다양한 전술적 패턴을 적용하는 것이다.
DDD 는 복잡한 비즈니스 로직을 효과적으로 관리하고, 유지보수성과 확장성이 뛰어난 시스템을 구축하는 데 매우 유용하다.
현재 마이크로서비스 아키텍처, CQRS (Command Query Responsibility Segregation), 이벤트 소싱 (Event Sourcing) 과 함께 사용되어 현대적인 분산 시스템 구축에 핵심적으로 활용되고 있다.
핵심 개념
Domain-Driven Design(DDD) 의 핵심 개념은 전략적 설계 (Strategic Design) 와 전술적 설계 (Tactical Design) 의 두 축을 기반으로 구성된다.
전략적 설계 (Strategic Design)
도메인 전체를 비즈니스 관점에서 논리적으로 분해하고, 시스템을 경계 (Context) 단위로 나누는 고수준 설계 전략.
개념 | 설명 |
---|---|
도메인 (Domain) | 소프트웨어 시스템이 해결하려는 특정 비즈니스 문제 영역 |
도메인 모델 (Domain Model) | 도메인의 개념, 규칙, 프로세스를 객체 중심으로 추상화한 구조 |
유비쿼터스 언어 (Ubiquitous Language) | 도메인 전문가와 개발자가 공동으로 사용하는 일관된 언어로, 모델·코드·문서에 모두 반영됨 |
바운디드 컨텍스트 (Bounded Context) | 도메인 모델이 명확하게 정의되어 적용되는 경계. 컨텍스트마다 모델이 다를 수 있음 |
하위 도메인 (Subdomain) | 도메인을 핵심 (Core), 지원 (Supporting), 일반 (General) 하위 도메인으로 분류 |
컨텍스트 매핑 (Context Mapping) | 여러 바운디드 컨텍스트 간의 관계 (Shared Kernel, Customer-Supplier 등) 를 정의하는 전략 |
전술적 설계 (Tactical Design)
각 Bounded Context 내부에서 도메인 모델을 어떻게 설계하고 구현할지에 관한 세부 설계 기법.
개념 | 설명 |
---|---|
엔티티 (Entity) | 고유 식별자 (ID) 를 가지며 상태 변화와 생명주기를 갖는 객체. 예: Order , User |
값 객체 (Value Object) | 고유 식별자가 없고 불변성을 갖는 객체. 값만으로 동등성 판단. 예: Money , Address |
애그리게이트 (Aggregate) | 여러 엔티티와 값 객체를 하나의 트랜잭션 단위로 묶는 집합. 루트 엔티티 (Aggregate Root) 를 통해서만 접근 |
도메인 서비스 (Domain Service) | 하나의 엔티티나 값 객체에 속하지 않는 도메인 로직을 담당하는 객체 |
도메인 이벤트 (Domain Event) | 도메인 내에서 발생한 의미 있는 상태 변화. 예: OrderPlaced , PaymentConfirmed |
리포지토리 (Repository) | 도메인 객체의 저장소로, 객체의 생성·조회·삭제를 추상화 |
팩토리 (Factory) | 복잡한 엔티티나 애그리게이트 생성을 담당하는 객체 또는 패턴 |
스펙리피케이션 (Specification) | 비즈니스 규칙이나 조건을 캡슐화하여 복잡한 쿼리나 필터링을 표현하는 도메인 로직 구성 방식 |
아키텍처 및 구현 관련 요소
개념 | 설명 |
---|---|
레이어드 아키텍처 (Layered Architecture) | Presentation, Application, Domain, Infrastructure 계층으로 관심사를 분리 |
헥사고날 아키텍처 (Hexagonal Architecture) | 내부 도메인을 외부와 격리하기 위해 포트 (Port) 와 어댑터 (Adapter) 를 사용하는 구조 |
클린 아키텍처 (Clean Architecture) | 의존성 역전을 기반으로 내부 도메인 (엔티티) 에 집중하는 계층화된 구조 |
CQRS (Command Query Responsibility Segregation) | 명령 (Command) 과 조회 (Query) 의 책임을 분리하여 성능 및 유지보수성 개선 |
이벤트 소싱 (Event Sourcing) | 상태 변경을 이벤트로 저장하여 시간 기반의 상태 추적 가능하게 하는 방식 |
안티 - 코럽션 레이어 (Anti-Corruption Layer) | 외부 시스템의 모델과 내부 도메인 모델 간의 변환 및 격리를 제공하는 계층 |
애플리케이션 서비스 (Application Service) | 도메인 객체를 조합하여 유스케이스를 실행하며, 트랜잭션과 오케스트레이션 담당 |
실무 적용 도구 및 협업 기법
개념 | 설명 |
---|---|
이벤트 스토밍 (Event Storming) | 도메인 이벤트를 중심으로 시스템 동작과 모델을 시각화하는 협업 워크숍 기법 |
도메인 스토리텔링 (Domain Storytelling) | 이해관계자가 참여하여 업무 프로세스를 이야기로 표현하는 협업 기반 모델링 방법 |
컨텍스트 매퍼 (Context Mapper) | DSL 기반 도구로 컨텍스트 간 관계 및 맵핑을 다이어그램화하고 자동화하는 도구 |
테스트 전략 | 단위 테스트 (도메인 객체 중심), 계약 테스트 (컨텍스트 간 API), 시나리오 테스트 중심 설계 필요 |
배경
DDD 는 2003 년 Eric Evans 의 저서 “Domain-Driven Design: Tackling Complexity in the Heart of Software” 를 통해 소개되었다. 복잡한 소프트웨어 개발에서 기술적 측면보다는 비즈니스 도메인에 집중해야 한다는 철학에서 출발했다.
DDD 는 기존의 데이터 중심 설계 (Data-Driven Design) 의 한계를 극복하기 위해 등장했다.
전통적인 소프트웨어 개발에서는 다음과 같은 문제점들이 있었다:
- 비즈니스 도메인에 대한 이해 부족: 기술적 구현에만 집중하여 실제 비즈니스 요구사항과 괴리
- 도메인 전문가와 개발자 간 소통 단절: 서로 다른 언어와 관점으로 인한 오해
- 복잡성 관리 실패: 시스템이 커질수록 유지보수가 어려워지는 구조
- 변화에 대한 취약성: 비즈니스 변화에 유연하게 대응하지 못하는 경직된 설계
목적 및 필요성
주요 목적
- 비즈니스 - 기술 격차 해소: 비즈니스와 소프트웨어 개발 간의 격차를 줄이고 기술 팀과 도메인 전문가 간의 명확한 소통 촉진
- 복잡성 관리: 복잡한 비즈니스 로직을 캡슐화하는 도메인 모델을 통해 비즈니스 현실과 코드 간의 격차 해소
- 시스템 모듈화: 대규모 도메인을 여러 Bounded Context 로 분할하여 명시적 관계 설정
필요성
- 레거시 시스템의 한계: 단일 통합 모델의 한계로 인한 Big Ball of Mud 아키텍처 방지
- 마이크로서비스 경계 정의: 마이크로서비스 아키텍처에서 서비스 경계를 정의하는 지침 제공
- 지속적인 진화: 변화하는 비즈니스 도메인에 맞춰 소프트웨어가 진화할 수 있는 구조 제공
주요 기능 및 역할
- 도메인 모델링: 비즈니스 로직과 규칙을 객체로 모델링
- 바운디드 컨텍스트 설계: 도메인별 경계 설정 및 독립적 개발
- 유비쿼터스 언어 적용: 공통 언어로 커뮤니케이션
- 애그리게이트 및 엔티티 설계: 비즈니스 규칙 중심의 객체 설계
- 도메인 서비스 및 리포지토리 구현: 비즈니스 로직과 데이터 접근 분리
전략적 설계 (Strategic Design)
- 도메인 분할: 큰 도메인을 하위 도메인으로 나누어 관리
- 바운디드 컨텍스트 식별: 모델의 일관성이 유지되는 경계 정의
- 컨텍스트 매핑: 바운디드 컨텍스트 간의 관계와 통합 방식 설계
전술적 설계 (Tactical Design)
- 도메인 모델링: 엔티티, 밸류 오브젝트, 애그리거트 설계
- 비즈니스 로직 구현: 도메인 서비스와 도메인 이벤트 활용
- 아키텍처 패턴 적용: 레이어드, 헥사고날 아키텍처 구현
특징
핵심 특징
- 도메인 중심성: 비즈니스 도메인이 설계의 중심
- 협업 지향: 도메인 전문가와 개발자의 긴밀한 협력
- 진화적 설계: 지속적인 학습과 개선을 통한 모델 발전
- 명시적 모델링: 암묵적 지식을 명시적 코드로 표현
구별되는 특징
- 유비쿼터스 언어: 모든 의사소통에서 동일한 용어 사용
- 바운디드 컨텍스트: 모델의 적용 범위를 명확히 제한
- 애그리거트: 일관성 경계를 명확히 정의
- 이벤트 중심 사고: 비즈니스 이벤트를 중심으로 모델링
핵심 원칙
DDD 의 핵심 원칙은 도메인 중심의 설계와 유비쿼터스 언어의 사용이다. 도메인 전문가와 개발자가 협업하여 도메인 모델을 구축하고, 이를 소프트웨어에 반영한다. 또한, 바운디드 컨텍스트를 정의하여 도메인 모델의 적용 범위를 명확히 한다.
DDD 의 3 가지 핵심 원칙
Focus on the Core Domain (핵심 도메인에 집중)
- 비즈니스 경쟁 우위를 제공하는 핵심 영역에 집중
- 제한된 자원을 가장 중요한 부분에 투자
Explore Models in Collaboration (협력을 통한 모델 탐구)
- 도메인 전문가와 개발자의 지속적 협력
- 공동 모델링을 통한 깊은 도메인 이해
Speak Ubiquitous Language (유비쿼터스 언어 사용)
- 모든 이해관계자가 동일한 언어 사용
- 코드와 대화에서 일관된 용어 유지
graph TD A[도메인 전문가] --> B[유비쿼터스 언어] C[개발자] --> B D[테스터] --> B E[비즈니스 분석가] --> B B --> F[코드] B --> G[문서] B --> H[대화] B --> I[테스트]
설계 원칙
- Model-Driven Design: 모델과 구현의 긴밀한 연결
- Hands-On Modelers: 모델러가 직접 코드 작성 참여
- Refactoring Toward Deeper Insight: 지속적 리팩토링을 통한 깊은 통찰
graph LR A[도메인 모델] <--> B[코드 구현] B <--> C[대화와 문서] C <--> A A -.-> D[지속적 리팩토링] D -.-> A
핵심 개념:
- 도메인 모델과 코드가 긴밀하게 연결
- 모델의 변화가 즉시 코드에 반영
- 지속적인 리팩토링을 통한 모델 개선
전략적 - 전술적 설계 분리
- 전략적 설계: 바운디드 컨텍스트, 유비쿼터스 언어, 컨텍스트 맵 등 시스템의 큰 구조를 정의한다.
- 전술적 설계: 엔티티, 값 객체, 애그리게이트, 도메인 서비스, 이벤트, 리포지토리 등 실질적 코드 구조를 설계한다.
graph LR A[Strategic Design] --> B[Tactical Design] subgraph Strategic C[Bounded Context] D[Context Map] E[Subdomain] end subgraph Tactical F[Aggregate] G[Entity] H[Value Object] I[Repository] end A --> Strategic B --> Tactical
DDD 개발 프로세스
sequenceDiagram participant DE as 도메인 전문가 participant DEV as 개발자 participant MODEL as 도메인 모델 participant CODE as 코드 DE->>DEV: 도메인 지식 공유 DEV->>MODEL: 초기 모델링 MODEL->>CODE: 코드 구현 CODE->>DEV: 구현을 통한 학습 DEV->>DE: 질문과 확인 DE->>DEV: 피드백 제공 DEV->>MODEL: 모델 개선 MODEL->>CODE: 리팩토링 Note over DE,CODE: 지속적인 협력과 개선
도메인 지식 공유 (DE ➡ DEV)
도메인 전문가 (Subject Matter Expert) 는 개발자에게 비즈니스의 규칙, 용어, 흐름 등 도메인 지식을 전달한다.
이는 유비쿼터스 언어 (Ubiquitous Language) 의 형성 및 모델링의 시작점이다.초기 모델링 (DEV ➡ MODEL)
개발자는 공유받은 도메인 지식을 바탕으로 개념 모델을 만듭니다. 이 모델은 엔티티, 값 객체, 애그리게이트 등을 포함한다.코드 구현 (MODEL ➡ CODE)
모델링된 구조를 바탕으로 실제 코드로 구현한다. 이 때, 애플리케이션 레이어, 도메인 레이어, 인프라스트럭처 레이어 등 계층적 아키텍처 설계를 따르게 된다.구현을 통한 학습 (CODE ➡ DEV)
코드를 작성하고 테스트하는 과정에서 개발자는 도메인에 대한 새로운 이해를 얻게 되고, 때로는 기존 이해에 오류가 있었음을 발견한다.질문과 확인 (DEV ➡ DE)
개발자는 도메인 전문가에게 다시 질문하거나 모델에 대한 확인을 요청한다. 이 과정은 도메인에 대한 해석 오류 방지를 위한 중요한 피드백 루프이다.피드백 제공 (DE ➡ DEV)
도메인 전문가는 잘못된 부분을 바로잡거나, 새로운 도메인 규칙 또는 예외 상황을 공유한다.모델 개선 (DEV ➡ MODEL)
피드백을 반영해 도메인 모델을 재구성하거나 수정한다. 이는 종종 이벤트 스토밍, 컨텍스트 매핑 등 협업 기법을 통해 이루어진다.리팩토링 (MODEL ➡ CODE)
개선된 도메인 모델에 따라 코드를 리팩토링한다. 이 과정은 기술 부채를 줄이고, 도메인 변경에 유연한 구조를 만드는데 핵심이다.
전략적 설계 (Strategic Design)
도메인 전체를 비즈니스 관점에서 논리적으로 분해하고, 시스템을 경계 (Context) 단위로 나누는 고수준 설계 전략.
주요 목적
- 도메인을 의미 있는 단위로 분리
- 팀 간 책임 범위 분할
- 모델 간 통합 관계 정의
핵심 구성요소
구성요소 | 설명 |
---|---|
Subdomain | 시스템 내 비즈니스 하위 영역 (Core, Supporting, Generic) 으로 분류됨 |
Bounded Context | 특정 모델이 일관되게 사용되는 경계. 설계·언어·의미가 명확히 정리됨 |
Context Map | 여러 Bounded Context 간의 관계를 정의하는 지도. 공유 커널, ACL, 고객 - 공급자 등 포함 |
예시:
- Core Subdomain: 결제 시스템
- Bounded Context:
Payment
,Invoice
- Context Map:
Payment ↔ Invoice
는Customer-Supplier
관계로 구성
Subdomain 의 세 가지 분류
구분 | 정의 | 특징 | 예시 |
---|---|---|---|
Core Subdomain | 시스템이 경쟁력을 갖기 위해 반드시 잘 설계되어야 하는 핵심 비즈니스 영역 | - 조직만의 차별화 요소 - 가장 많은 투자가 이루어져야 함 - 고급 도메인 전문가와의 협업 필수 | 주문 처리 로직 (전자상거래), 대출 승인 알고리즘 (금융), 물류 최적화 (배송) |
Supporting Subdomain | Core 를 지원하지만, 핵심 가치는 아니며 핵심 기능의 성능이나 유효성을 보완 | - 외부 구매 가능성 낮음 - 복잡할 수 있으나 전략적 가치 낮음 - 위임하거나 내재화 가능 | 사용자 관리, 권한 시스템, 알림 처리, 보고서 생성 |
Generic Subdomain | 비즈니스와 무관한 공통 인프라 영역으로 범용적이고 재사용 가능한 부분 | - 상용 솔루션 구매 가능 - 직접 구현 비효율적 - 비즈니스 경쟁력과 직접 연관 없음 | 이메일 발송 시스템, 결제 게이트웨이, 인증 시스템, 로깅, DB 접속 모듈 |
바운디드 컨텍스트 간 통합 패턴
graph TD subgraph BoundedContextA A1[Entity] A2[Value Object] A3[Aggregate Root] A4[Domain Service] A5[Repository] A6[Domain Event] A1 --> A3 A2 --> A3 A3 --> A4 A3 --> A5 A4 --> A6 end subgraph BoundedContextB B1[Entity] B2[Value Object] B3[Aggregate Root] B1 --> B3 B2 --> B3 end A3 -. Context Map .-> B3
Bounded Context 간의 관계와 내부 구조를 시각화한 것으로, 각 컨텍스트는 독립적으로 도메인을 모델링하며, 상호 통합 시 Context Map을 통해 연결된다.
Bounded Context A 의 구성:
요소 | 역할 설명 |
---|---|
Entity (A1) | 고유 식별자를 가진 객체. 도메인 내에서 상태 변화와 생명주기를 가짐 |
Value Object (A2) | 값으로만 구분되는 불변 객체 (예: 주소, 통화 등) |
Aggregate Root (A3) | 트랜잭션 및 일관성 경계를 대표하는 루트 엔티티. 외부 접근은 이 루트를 통해서만 가능 |
Domain Service (A4) | 특정 엔티티에 속하지 않는 도메인 로직을 캡슐화 |
Repository (A5) | Aggregate 를 저장하고 조회하는 인터페이스. 인프라 추상화 역할 |
Domain Event (A6) | 도메인 상태 변화 발생 시 발행되는 이벤트 (예: 주문 생성됨, 결제 완료됨 등) |
→ 각 요소는 다음과 같이 연결된다:
- Entity 와 Value Object 는 Aggregate Root 와 연결.
- Aggregate Root 는 비즈니스 로직 (Domain Service), 영속화 (Repository) 와 연결됨.
- Domain Service 에서 중요한 상태 변화가 발생하면 Domain Event 가 발행됨.
Bounded Context B 의 구성:
- Entity (B1) 와 Value Object (B2) 는
→ Aggregate Root (B3) 아래에서 함께 모델링되어 있음.
→ 이는 하나의 다른 서브 도메인 또는 마이크로서비스가 독립적으로 설계된 구조를 보여준다.
Context Map 에 의한 연결
A3 -. Context Map.-> B3
는 Bounded Context A 의 Aggregate Root 와 B 의 Aggregate Root 가 연결됨을 의미한다.- 이 연결은 Context Map의 일종으로, 다음과 같은 전략들이 있을 수 있다:
맵핑 전략 | 설명 |
---|---|
Shared Kernel | 두 컨텍스트가 일부 공통 모델을 공유함. 협력 필요성 높음 |
Customer/Supplier | 한쪽이 주도하고 다른 한쪽이 API 또는 이벤트를 소비 |
Conformist | 한쪽이 다른 쪽 모델이나 계약을 그대로 따름 |
Anti-Corruption Layer (ACL) | 서로 다른 모델 간 통합 시 번역 계층을 두어 오염 방지 |
→ 위 다이어그램에서는 컨텍스트 간 직접 연결된 점선이 있으므로, 일반적으로 ACL 또는 Conformist 전략을 사용하는 경우에 해당할 수 있다.
전술적 설계 (Tactical Design)
각 Bounded Context 내부에서 도메인 모델을 어떻게 설계하고 구현할지에 관한 세부 설계 기법.
주요 목적:
- 도메인 모델을 코드로 명확하게 구현
- 복잡한 도메인 로직을 모듈화
- 시스템 복잡성 감소 및 유지보수성 향상
핵심 구성요소
구성요소 | 설명 |
---|---|
Entity | 고유 식별자를 가진 객체. 생명주기를 갖고, 상태와 행위 포함 |
Value Object | 값으로만 비교되는 불변 객체. 예: Money, Address |
Aggregate | 일관성 경계를 가진 객체의 집합. 루트를 통해 접근 |
Domain Service | 특정 Entity 에 속하지 않는 복잡한 도메인 로직 수행 |
Domain Event | 도메인에서 발생한 중요한 사건 모델링 |
Repository | Aggregate 의 영속성 처리 인터페이스 |
Factory (선택) | 복잡한 객체 생성 로직을 분리하여 표현 |
Specification (선택) | 비즈니스 규칙을 조합 가능하고 재사용 가능한 형태로 표현 |
Domain-Driven Design 통합 아키텍처
graph TD %% SYSTEM 구성 A[System] --> B[Subdomains] B --> C1[Core Subdomain] B --> C2[Supporting Subdomain] B --> C3[Generic Subdomain] %% Bounded Context와 Subdomain 연결 C1 --> BC1[Bounded Context A] C2 --> BC2[Bounded Context B] C3 --> BC3[Bounded Context C] %% Context Map 관계 subgraph ContextMap[Context Map] BC1 <-->|Shared Kernel| BC2 BC2 <-->|Customer-Supplier| BC3 BC1 -.->|Anti-Corruption Layer| BC3 end %% Bounded Context A 내부 계층 구조 subgraph BoundedContextA direction TB UI[Presentation Layer] UI --> App[Application Layer] App --> AL1[Application Service] AL1 --> DL[Domain Layer] DL --> AR[Aggregate Root] AR --> E[Entity] AR --> V[Value Object] DL --> DS[Domain Service] DL --> DE[Domain Event] DL --> RI[Repository Interface] DL --> SP[Specification] DL --> F[Factory] App --> INF[Infrastructure Layer] INF --> RImpl[Repository Implementation] INF --> Adapter["External Adapter (API, Messaging)"] INF --> DB[(Database)] end %% 설명 연결 (선택) click ContextMap href "https://martinfowler.com/bliki/ContextMap.html" "Context Map 설명 보기" click B href "https://dddcommunity.org/library/" "Subdomain 설명 보기"
Domain-Driven Design 의 Layered Architecture
DDD 는 일반적으로 다음과 같은 4 계층 구조를 따른다:
- 프레젠테이션 계층 (Presentation Layer)
- 응용 계층 (Application Layer)
- 도메인 계층 (Domain Layer)
- 인프라스트럭처 계층 (Infrastructure Layer)
이 구조를 통해 각 계층은 단일 책임을 가지며, 하위 계층에 대한 의존성만을 가진다.
계층 | 구성 요소 | 기능 및 역할 |
---|---|---|
1. 프레젠테이션 계층 (Presentation Layer) | - 컨트롤러 (Controller) - 뷰 (View) DTO (Data Transfer Object) | - 사용자 요청 처리 및 응답 생성 - 입력 데이터 유효성 검증 - 응용 계층과 인터페이스 |
2. 응용 계층 (Application Layer) | - 응용 서비스 (Application Service) - 명령 핸들러 (Command Handler) - 쿼리 핸들러 (Query Handler) | - 비즈니스 워크플로 조정 - 트랜잭션 경계 정의 - 도메인 객체 간 협력 조정 |
3. 도메인 계층 (Domain Layer) | 필수: - 엔티티 (Entity) - 밸류 오브젝트 (Value Object) - 애그리거트 (Aggregate) - 도메인 서비스 (Domain Service) - 리포지토리 인터페이스 (Repository Interface) 선택: - 도메인 이벤트 (Domain Event) - 팩토리 (Factory) - 명세 (Specification) | - 핵심 비즈니스 로직 구현 - 비즈니스 규칙과 제약 적용 - 도메인 지식 표현 |
4. 인프라스트럭처 계층 (Infrastructure Layer) | - 리포지토리 구현체 - 외부 서비스 어댑터 - 메시징 시스템 | - 기술적 세부 구현 - 외부 시스템 연동 - 데이터 저장 및 조회 |
도메인 계층 (Domain Layer)
Domain Layer는 DDD(도메인 주도 설계) 의 아키텍처 계층 중 하나로, 핵심 비즈니스 로직, 규칙, 정책, 제약을 코드로 명시적으로 구현하는 계층을 의미한다.
이 계층은 시스템이 해결하려는 " 도메인 문제 영역 " 의 본질을 표현하며, 기술적인 세부사항과는 철저히 분리되어야 한다.
역할과 특징:
항목 | 설명 |
---|---|
🎯 비즈니스 중심 | 소프트웨어가 해결하려는 핵심 비즈니스 규칙, 상태, 행위를 모델링한다. |
🧩 전술적 설계 기반 | Entity, Value Object, Aggregate 등 DDD 전술 패턴의 주요 구성요소들이 위치한다. |
📦 도메인 모델의 집합소 | 도메인 모델 (Domain Model) 을 담고 있으며, 도메인의 개념적 구조를 코드로 표현한다. |
💡 기술 독립성 | 영속성 (DB), 메시징, API 등 기술 구현 세부사항에서 완전히 분리되어 있어야 한다. |
🔁 변화 유연성 | 기술이 바뀌더라도 도메인 모델은 최대한 변경되지 않도록 설계된다. |
주요 구성 요소:
구성 요소 | 설명 | 특징 | 역할 | 예시 |
---|---|---|---|---|
Entity | 고유 식별자 (ID) 를 가진 도메인 객체. 상태와 생명주기를 가지며 변경 가능. | - ID 기반 동등성 - 상태 보존 - 생명주기 존재 | - 핵심 비즈니스 객체 표현 - 상태 및 행위 관리 | User , Order , Account |
Value Object | 값 자체로 의미가 있으며 불변성을 가지는 객체. 식별자 없이 모든 속성으로 동등성 판단. | - 불변 (immutable) - ID 없음 - 값 기반 동등성 | - 값 표현 - 조합 가능, 테스트 용이 | Money , Address , Email |
Aggregate | 하나 이상의 Entity/Value Object 를 묶는 일관성 경계. 루트로만 접근 허용. | - 트랜잭션 일관성 보장 - 루트 외부 접근 제한 - 복합 도메인 단위 | - 비즈니스 규칙 단위 - 내부 일관성 관리 - Aggregate Root 제공 | Order (루트) + OrderItem |
Domain Service | Entity 나 VO 에 속하지 않는 순수 도메인 로직을 처리하는 무상태 객체. | - Stateless - 비즈니스 로직 응집 - 여러 Aggregate 사용 가능 | - 외부 로직 캡슐화 - 복합 연산 수행 - 규칙/조건 검증 | TransferService , TaxCalculator |
Domain Event | 도메인 상태 변화나 의미 있는 사건을 표현하는 불변 객체. | - 발생 시점 기록 - 비동기 처리 용이 - 불변 객체 | - 로깅, 통지, 트리거 - 이벤트 기반 처리 유도 | UserRegisteredEvent , OrderShippedEvent |
Repository Interface | Aggregate 단위로 저장/조회하는 추상화된 영속성 인터페이스. | - 기술 독립성 유지 - 의존 역전 - 도메인 계층 분리 가능 | - Aggregate 저장/조회 - 도메인 계층 내 기술 분리 | OrderRepository , UserRepository |
Specification (선택) | 비즈니스 규칙을 명시적 객체로 분리하여 재사용/조합 가능하게 표현. | - 재사용성 - 명세 캡슐화 - 조합 가능 (AND/OR 등) | - 조건 로직 추상화 - 복잡한 필터/조건 캡슐화 | AvailableProductSpec , CustomerIsVIPSpec |
Factory (선택) | 복잡한 객체 생성 로직을 캡슐화하고 생성을 단순화하기 위한 책임 분리 객체. | - 생성 책임 단일화 - 복잡한 생성 절차 추상화 - 재사용성 높음 | - 복합 Aggregate/Entity 생성 - 객체 생성을 도메인 계층에 포함시키지 않음 | OrderFactory , AccountFactory |
전략적 설계 (Strategic Design) vs. 전술적 설계 (Tactical Design)
Strategic Design은 무엇을 왜 구축할지에 대한 비즈니스 도메인의 전체적인 구조와 경계를 정의하는 고수준 설계 접근법이며, Tactical Design은 어떻게 구현할지에 대한 도메인 모델의 구체적인 구현을 위한 저수준 패턴과 기법을 제공한다.
- 전략적 설계 (Strategic Design) 를 통해 무엇을 만들고 누구와 협업할지를 설계한다.
- 도메인 구조 설계, 팀/시스템 분할, 경계 정의를 통해 시스템의 큰 그림을 설계한다.
- 전술적 설계 (Tactical Design) 를 통해 그것을 어떻게 구현할지 를 구체화해야 한다.
- 각 경계 내에서 도메인 모델을 구현하고, 객체지향 패턴을 적용하여 비즈니스 로직을 실제 코드로 옮긴다.
- Context Map 과 Ubiquitous Language 는 전략적 + 전술적 설계 사이의 연결 고리 역할을 한다.
DDD 설계 접근 방식 비교
구분 | Strategic Design (전략적 설계) | Tactical Design (전술적 설계) |
---|---|---|
핵심 질문 | 무엇을 만들 것인가? 왜 존재하는가? | 어떻게 만들 것인가? 어떤 구조로 구현할 것인가? |
목적 | 도메인 경계 정의 및 팀 간 협업 기반 수립 | 도메인 모델의 구현 및 내부 설계 |
적용 시점 | 프로젝트 초기 (기획/분석/설계 단계) | 개발 및 구현 단계 |
적용 대상 범위 | 전체 시스템, 다중 도메인 및 컨텍스트 | 단일 Bounded Context 내부 |
참여자 | 도메인 전문가, 아키텍트, 비즈니스 분석가 | 개발자, 소프트웨어 엔지니어 |
주요 산출물 | - Subdomain 분리 - Bounded Context 정의 - Context Map - 공유 언어 (Ubiquitous Language) | - Aggregate, Entity, Value Object - Repository, Factory, Service - 도메인 코드와 테스트 |
추상화 수준 | 고수준 (High-level 구조 설계) | 저수준 (Low-level 객체 설계 및 구현) |
설계 관점 | 조직 및 아키텍처 구조 중심 | 코드와 구현 중심 |
주요 기법 | - Event Storming - Domain Storytelling - Context Mapping | - 객체지향 모델링 - DDD 빌딩 블록 (Entity, VO 등) - 디자인 패턴 적용 |
사용 도구 | 화이트보드, 협업 도구, 모델링 툴 (Miro, Lucidchart 등) | IDE, 테스트 프레임워크, UML 도구 |
장점 | - 도메인 중심의 명확한 시스템 구조 - 팀 협업 기반 강화 - 비즈니스 전략과 시스템 일치 | - 도메인 로직의 일관성 유지 - 코드 품질 향상 - 유지보수 용이성 |
단점 | - 초기 비용과 시간 소요 큼 - 협업 부족 시 실패 가능성 | - 도메인 경계 불명확 시 코드 중복, 품질 저하 |
대표 개체 | - Subdomain - Bounded Context - Context Map - Ubiquitous Language | - Entity - Value Object - Aggregate - Repository - Domain Service |
도전 과제 | - 팀 간 경계 협의 - 복잡한 도메인의 모듈화 - 레거시 통합 | - 설계와 구현의 정합성 유지 - 복잡한 도메인 모델링 - 테스트 자동화 |
반복 주기 | 장기적 (월, 분기 단위 구조 조정) | 단기적 (일, 주 단위 개발 반복) |
핵심 원칙 | - 도메인 중심 사고 - 명확한 경계 - 모듈화와 분리 | - 모델 불변성 - 패턴 기반 구현 - 테스트 용이성 |
강점과 약점
강점
관점 | Strategic Design | Tactical Design |
---|---|---|
비즈니스 정렬 | 비즈니스 요구사항과 높은 정렬성 | 명확한 비즈니스 로직 표현 |
팀 협업 | 팀 간 명확한 경계 설정, 자율성 확보 | 개발자 간 코드 구조 일관성 유지 |
유지보수성 | 장기적으로 안정적인 아키텍처 설계 가능 | 코드 품질 향상, 테스트 용이성, 재사용 가능한 도메인 모델 구성 |
확장성 | 도메인 중심 시스템 확장 시 경계 기반 설계 가능 | 모듈화된 구조로 유연한 기능 추가 및 변경 용이 |
시스템 구조 | 전체 시스템의 큰 그림 설계에 유리 | 실제 코드와 동작 구현에 직접적으로 연결되는 모델링 수행 가능 |
약점
관점 | Strategic Design | Tactical Design |
---|---|---|
초기 비용 | 높은 진입 장벽, 도입 초기에 많은 분석 시간 소요 | 패턴 이해 및 적용에 학습 비용 존재 |
도메인 정렬 | 도메인 전문가의 참여 없이는 의미 있는 모델 정립 어려움 | 전체적인 맥락 없이 세부 구현만 집중할 경우 설계 왜곡 가능 |
협업 이슈 | 분석이 늦어지거나 의사결정 지연 발생 가능 | 컨텍스트 간 일관성 유지가 어려울 수 있음 |
복잡도 관리 | 경계를 지나치게 나누면 조직 내 협업 복잡도 증가 가능 | 과도한 전술적 패턴 적용 시 오히려 시스템 복잡도 증가 가능 |
유지보수 위험성 | 변경 시 연쇄적인 설계 영향 범위가 넓어질 수 있음 | 경계 무시하거나 패턴 오용 시 설계 일관성 및 유지보수성 저하 가능성 존재 |
시각적 개요 흐름
graph TD A[Strategic Design] --> B[Subdomain 분석] B --> C[Bounded Context 정의] C --> D[Context Map 설계] D --> E[Ubiquitous Language 정립] E --> F[Tactical Design 진입] F --> G[Entity/VO/Aggregate 설계] G --> H[Repository/Service 적용] H --> I[테스트 및 구현 반복]
DDD Strategic ↔ Tactical 설계
도메인 주도 설계 (DDD) 에서 전략적 설계와 전술적 설계 간의 복잡하고 상호의존적인 관계를 체계적으로 나타낸다.
다이어그램의 최상단에서 시작하는 비즈니스 도메인은 전략적 설계의 출발점이 되며, 여기서 핵심 도메인, 지원 도메인, 일반 도메인으로 세분화된 서브도메인들이 식별된다. 이러한 서브도메인들은 경계 컨텍스트로 구체화되며, 각 컨텍스트는 고유한 유비쿼터스 언어를 통해 일관된 모델을 형성한다. 컨텍스트 맵은 이러한 경계 컨텍스트들 간의 관계와 통합 패턴을 명시적으로 정의한다.
전략적 설계에서 정의된 경계 컨텍스트는 전술적 설계의 핵심 진입점이 된다. 전술적 설계에서는 애그리게이트 루트가 중심적 역할을 수행하며, 이를 통해 엔티티, 값 객체, 도메인 이벤트들이 일관성 있게 관리된다. 애그리게이트 외부에는 도메인 서비스, 저장소 인터페이스, 팩토리, 모듈, 명세 패턴 등이 위치하여 애그리게이트와 협력한다.
구현 과정에서 발견되는 모델의 복잡성이나 한계는 다시 경계 컨텍스트의 재정의나 유비쿼터스 언어의 정제로 이어진다. 이러한 반복적 개선 과정은 DDD 의 핵심 철학인 지속적인 학습과 진화를 구현한다.
구현 계층에서는 헥사고날 아키텍처 원칙에 따라 REST API/UI, 애플리케이션 계층, 도메인 모델 계층, 인프라스트럭처 계층이 명확히 분리된다. 도메인 모델 계층은 전술적 설계의 핵심 요소들을 포함하며, 애플리케이션 계층은 저장소 인터페이스를 통해 도메인과 인프라스트럭처를 연결한다.
컨텍스트 간 통합은 다양한 패턴을 통해 이루어지며, 도메인 이벤트는 컨텍스트 간 비동기 통신의 핵심 메커니즘으로 작동한다. 이를 통해 각 컨텍스트의 독립성을 유지하면서도 필요한 협력을 가능하게 한다.
graph TD %% 전략적 설계 A[📌 비즈니스 도메인] --> B["🧠 전략적 설계 (Strategic Design)"] B --> C1[📂 Core/Supporting/Generic Subdomains] B --> C2[🔲 Bounded Contexts] B --> C3[🗺️ Context Map] B --> C4[🗣️ Ubiquitous Language] %% Strategic에서 Tactical로의 연결 C2 --> D["🔧 전술적 설계 (Tactical Design)"] C4 -.-> D %% Tactical Design - Aggregate 중심 구조 D --> E1[🧱 Aggregate Root] E1 --> E2[🧩 Entity] E1 --> E3[📦 Value Object] E1 --> E6[🧨 Domain Event] %% Aggregate 외부 패턴들 D --> E4[⚙️ Domain Service] D --> E5[📂 Repository Interface] D --> E7[🏭 Factory] D --> E8[📁 Module] D --> E9[📐 Specification] %% Repository 관계 E5 -.-> E1 E7 -.-> E1 %% Context 간 관계 C2 -->|Integration Patterns| F1[🔁 ACL / OHS / Conformist] C2 -->|Partnership/Customer-Supplier| F2[⬆️⬇️ Context Relations] %% 구현 계층 구조 (Hexagonal Architecture) subgraph "Implementation Layers" REST[🌐 REST API / UI] APP[📱 Application Layer] DOM[🏗️ Domain Model Layer] INFRA[💽 Infrastructure Layer] end %% 계층 간 관계 REST --> APP APP --> DOM APP --> E5 INFRA --> E5 %% Domain Model 내부 구조 DOM --> E1 DOM --> E4 DOM --> E8 %% 피드백 루프 (중요!) D -.->|"모델 개선 피드백"| C2 D -.->|"언어 정제 피드백"| C4 %% 이벤트 기반 통합 E6 -.->|"Context 간 통신"| F1 %% 스타일링 classDef strategic fill:#e1f5fe classDef tactical fill:#f3e5f5 classDef implementation fill:#e8f5e8 classDef integration fill:#fff3e0 class A,B,C1,C2,C3,C4 strategic class D,E1,E2,E3,E4,E5,E6,E7,E8,E9 tactical class REST,APP,DOM,INFRA implementation class F1,F2 integration
Strategic Design 구성 요소
구성 요소 | 영문명 | 역할 및 목적 | 주요 특징 | 산출물 |
---|---|---|---|---|
하위 도메인 | Core/Supporting/Generic Subdomains | 비즈니스 도메인을 복잡성과 전략적 중요도에 따라 분류 | • Core: 핵심 경쟁력 • Supporting: 지원 기능 • Generic: 범용 기능 | 도메인 분류 매트릭스 |
경계 컨텍스트 | Bounded Contexts | 특정 도메인 모델이 유효한 명확한 경계 설정 | • 모델 일관성 보장 • 팀 자율성 확보 • 기술 스택 독립성 | Context 경계 정의서 |
컨텍스트 맵 | Context Map | Bounded Context 간의 관계와 통합 방식 정의 | • 의존성 관계 시각화 • 통합 패턴 명시 • 팀 간 협업 구조 | Context Map 다이어그램 |
유비쿼터스 언어 | Ubiquitous Language | 도메인 전문가와 개발팀이 공유하는 공통 용어체계 | • 의사소통 명확화 • 코드 - 비즈니스 정렬 • 지속적 진화 | 용어 사전, 도메인 모델 |
Tactical Design 구성 요소
구성 요소 | 영문명 | 역할 및 목적 | 관계성 | 구현 특징 |
---|---|---|---|---|
집합체 루트 | Aggregate Root | 집합체의 진입점이며 데이터 일관성의 경계 | 다른 모든 tactical 요소의 중심 | • 고유 식별자 • 불변식 보장 • 트랜잭션 경계 |
엔티티 | Entity | 고유한 식별자를 가진 도메인 객체 | Aggregate 내부에 포함 | • 생명주기 관리 • 상태 변화 추적 • 비즈니스 행동 캡슐화 |
값 객체 | Value Object | 식별자 없이 값으로만 정의되는 불변 객체 | Aggregate 내부에 포함 | • 불변성 • 값 기반 동등성 • 도메인 개념 명시화 |
도메인 서비스 | Domain Service | 여러 도메인 객체에 걸친 비즈니스 로직 | Aggregate 를 조작 | • 무상태성 • 복잡한 비즈니스 규칙 • 도메인 로직 집중 |
저장소 | Repository | 도메인 객체의 저장 및 조회 인터페이스 | Aggregate Root 와 1:1 대응 | • 컬렉션 유사 인터페이스 • 인프라 추상화 • 도메인 중심 설계 |
팩토리 | Factory | 복잡한 객체 생성 로직 캡슐화 | Aggregate 생성 담당 | • 생성 복잡성 숨김 • 불변식 보장 • 생성 로직 집중화 |
도메인 이벤트 | Domain Event | 도메인에서 발생한 중요한 비즈니스 사건 | Aggregate 에서 발행 | • 비동기 처리 • 시스템 간 결합도 감소 • 이벤트 기반 아키텍처 |
모듈 | Module | 관련된 도메인 개념들의 논리적 그룹화 | 여러 Aggregate 포함 | • 코드 구조화 • 네비게이션 개선 • 관심사 분리 |
명세 | Specification | 복잡한 비즈니스 규칙을 객체로 표현 | 도메인 서비스에서 활용 | • 규칙 재사용성 • 조합 가능성 • 테스트 용이성 |
Tactical Design 관계 구조
Aggregate 중심 구조:
계층별 책임 분할:
계층 | 포함 요소 | 주요 책임 |
---|---|---|
도메인 모델 계층 | Aggregate, Entity, Value Object, Domain Event | 순수 비즈니스 로직 |
도메인 서비스 계층 | Domain Service, Specification | 복잡한 도메인 연산 |
애플리케이션 계층 | Repository Interface 사용 | 유스케이스 조정 |
인프라스트럭처 계층 | Repository Implementation | 기술적 구현 |
graph LR subgraph "Aggregate Boundary" AR[Aggregate Root] E[Entity] VO[Value Object] DE[Domain Event] end subgraph "Supporting Patterns" DS[Domain Service] R[Repository] F[Factory] S[Specification] end AR --> E AR --> VO AR --> DE DS -.-> AR R -.-> AR F -.-> AR S -.-> AR
Strategic-Tactical 상호작용 메커니즘
도메인 주도 설계 (DDD) 에서 Strategic Design 과 Tactical Design 의 관계는 서로 얽혀있으면서 지속적으로 상호작용하며 진화하는 구조이다.
전략적 설계부터 시작하여 전술적 설계를 따르며, 가장 큰 도메인 모델 설계의 깨달음과 돌파구는 전술적 설계 중에 일어날 가능성이 높고, 이는 다시 전략적 설계에 영향을 미치므로 이 과정을 반복한다.
하향식 영향 (Strategic → Tactical)
Strategic Design 에서 Tactical Design 으로의 정보 흐름은 변환 (Translation) 과정을 통해 이루어진다. Strategic Design 에서 Tactical Design 으로의 변환은 추상적인 비즈니스 개념을 구체적인 코드 구조로 번역하는 핵심 과정이다.
구체적 변환 매핑:
Strategic 요소 | Tactical 영향 | 구체적 변환 |
---|---|---|
Subdomain 분류 | Aggregate 우선순위 | Core Domain 의 Aggregate 는 더 정교한 모델링 |
Bounded Context | Module 구조 | 하나의 Context = 하나의 Module 그룹 |
Ubiquitous Language | 클래스/메서드 명명 | 도메인 용어가 직접 코드에 반영 |
Context Map | Integration Event | Context 간 통신을 위한 Domain Event 설계 |
도메인 모델의 각 Bounded Context 에 대해 도메인을 모델링하는 엔티티, 값 객체 및 집합체를 식별하고 정의한다. 컨텍스트를 정의하는 경계 내에 포함된 도메인 모델을 구축하고 개선한다.
단계별 변환 과정
변환 단계:
- 도메인 탐색 → Bounded Context 식별 → Aggregate 후보 발견
- Ubiquitous Language → Entity/Value Object 명명 → 메서드 시그니처 정의
- Context Map → Integration Pattern → Domain Event 설계
- Subdomain 분류 → Module 구조 → Package/Namespace 조직화
1 단계: 도메인 탐색 → Bounded Context 식별 → Aggregate 후보 발견
무엇을 만들 것인가? 에서 어떻게 나눌 것인가? 로의 변환
핵심 활동:
- 도메인 탐색 (Domain Exploration): 비즈니스 프로세스와 규칙 이해
- Bounded Context 식별: 일관된 모델 경계 정의
- Aggregate 후보 발견: 트랜잭션 일관성 단위 식별
입력물 (Input)
항목 | 설명 | 예시 |
---|---|---|
비즈니스 요구사항 | 도메인 전문가가 정의한 핵심 기능 | " 고객이 상품을 주문하고 결제한다 " |
도메인 지식 | 업무 프로세스와 비즈니스 규칙 | " 재고가 없으면 주문할 수 없다 " |
이해관계자 인터뷰 | 각 영역별 전문가 의견 | 주문팀, 배송팀, 결제팀 인터뷰 |
출력물 (Output)
항목 | 설명 | 예시 |
---|---|---|
Bounded Context Map | 컨텍스트 간 관계도 | Order ↔ Payment ↔ Shipping |
Aggregate 후보 목록 | 일관성 경계별 객체 그룹 | Order(OrderItem, Address), Product(Category, Price) |
Context 책임 정의 | 각 컨텍스트의 역할과 범위 | Order Context: 주문 생성/수정/취소 담당 |
핵심 의사결정:
- 컨텍스트 경계: 어디서 모델을 분리할 것인가?
- Aggregate 크기: 어떤 객체들을 함께 묶을 것인가?
- 일관성 요구사항: 어떤 데이터가 함께 변경되어야 하는가?
2 단계: Ubiquitous Language → Entity/Value Object 명명 → 메서드 시그니처 정의
비즈니스 용어를 코드 요소로 직접 번역하는 단계
핵심 활동:
- Ubiquitous Language 적용: 비즈니스 용어를 클래스/메서드명으로 변환
- Entity/Value Object 구분: 식별자 유무에 따른 객체 분류
- 메서드 시그니처 정의: 비즈니스 행동을 메서드로 구체화
입력물 (Input):
항목 | 설명 | 예시 |
---|---|---|
비즈니스 용어집 | 도메인에서 사용하는 핵심 용어 | " 주문 취소 “, " 배송 시작 “, " 결제 승인 " |
비즈니스 규칙 | 도메인의 제약사항과 정책 | " 결제 후에만 배송 가능 " |
워크플로우 | 업무 처리 흐름 | 주문 → 결제 → 배송 → 완료 |
출력물 (Output):
항목 | 설명 | 예시 |
---|---|---|
Entity 클래스 설계 | 식별자를 가진 도메인 객체 | class Order { void cancel(CancellationReason) } |
Value Object 설계 | 불변 값 객체 | class Money { Money add(Money other) } |
메서드 시그니처 | 비즈니스 행동의 구체적 표현 | PaymentResult approve(Amount, PaymentMethod) |
핵심 의사결정:
- Entity vs Value Object: 식별자가 필요한가?
- 메서드 명명: 비즈니스 용어를 어떻게 반영할 것인가?
- 매개변수 설계: 어떤 정보가 필요한가?
3 단계: Context Map → Integration Pattern → Domain Event 설계
컨텍스트 간 관계를 구체적 통합 메커니즘으로 변환하는 단계
핵심 활동:
- Context Map 해석: 컨텍스트 간 관계 분석
- Integration Pattern 선택: 적절한 통합 방식 결정
- Domain Event 설계: 비동기 통신 이벤트 정의
입력물 (Input):
항목 | 설명 | 예시 |
---|---|---|
Context Map | 컨텍스트 간 관계도 | Order → Payment (Customer/Supplier) |
데이터 흐름 요구사항 | 컨텍스트 간 정보 교환 필요성 | 주문 생성 시 결제 서비스 호출 |
성능/확장성 요구사항 | 비기능적 요구사항 | 주문 처리량: 1000 건/분 |
출력물 (Output):
항목 | 설명 | 예시 |
---|---|---|
Integration Pattern | 컨텍스트 통합 방식 | Anti-Corruption Layer, Domain Events |
Domain Event 명세 | 비즈니스 이벤트 정의 | OrderPlacedEvent , PaymentCompletedEvent |
API 인터페이스 | 동기 통합을 위한 인터페이스 | PaymentService.processPayment() |
핵심 의사결정:
- 동기 vs 비동기: 실시간 응답이 필요한가?
- 강결합 vs 약결합: 컨텍스트 독립성이 중요한가?
- 일관성 수준: 즉시 일관성 vs 최종 일관성
4 단계: Subdomain 분류 → Module 구조 → Package/Namespace 조직화
도메인 분류를 물리적 코드 구조로 변환하는 최종 단계
핵심 활동:
- Subdomain 분류 적용: Core/Supporting/Generic 우선순위 반영
- Module 구조 설계: 패키지/네임스페이스 조직화
- 코드 구조 정의: 디렉터리 및 파일 구조 결정
입력물 (Input):
항목 | 설명 | 예시 |
---|---|---|
Subdomain 분류 | 비즈니스 우선순위 분류 | Core: Order, Supporting: Customer, Generic: Payment |
아키텍처 제약사항 | 기술적 제약과 표준 | Java 패키지 명명 규칙 |
팀 구조 | 개발팀 조직과 책임 | Order 팀, Payment 팀 |
출력물 (Output):
항목 | 설명 | 예시 |
---|---|---|
Package 구조 | 코드 조직화 방식 | com.company.order.domain |
Module 의존성 | 모듈 간 관계 정의 | Order → Customer (readonly) |
배포 단위 | 물리적 배포 구조 | JAR, Docker Container |
조직화 원칙:
- 도메인 우선: 기술 레이어보다 도메인 개념으로 그룹화
- 응집성: 함께 변경되는 코드를 함께 배치
- 낮은 결합도: 모듈 간 의존성 최소화
단계별 변환 과정 예시: 전자상거래 도메인 분석
변환 단계:
- 도메인 탐색 → Bounded Context 식별 → Aggregate 후보 발견
- Ubiquitous Language → Entity/Value Object 명명 → 메서드 시그니처 정의
- Context Map → Integration Pattern → Domain Event 설계
- Subdomain 분류 → Module 구조 → Package/Namespace 조직화
1 단계: 도메인 탐색 → Bounded Context 식별 → Aggregate 후보 발견
핵심 활동:
- 도메인 탐색 (Domain Exploration): 비즈니스 프로세스와 규칙 이해
- Bounded Context 식별: 일관된 모델 경계 정의
- Aggregate 후보 발견: 트랜잭션 일관성 단위 식별
도메인 탐색 결과:
- 핵심 비즈니스 프로세스: 상품 진열 → 장바구니 → 주문 → 결제 → 배송
- 도메인 전문가 식별: 상품 관리자, 주문 관리자, 배송 담당자, 고객 서비스
Bounded Context 식별:
Aggregate 후보 발견:
Bounded Context | Aggregate Root | 포함 Entity/Value Object |
---|---|---|
Product Catalog | Product | Category, Price, ProductImage |
Shopping Cart | Cart | CartItem, Money |
Order Management | Order | OrderItem, ShippingAddress, BillingAddress |
Payment | Payment | PaymentMethod, Money, TransactionResult |
Shipping | Shipment | TrackingNumber, DeliveryAddress |
2 단계: Ubiquitous Language → Entity/Value Object 명명 → 메서드 시그니처 정의
핵심 활동:
- Ubiquitous Language 적용: 비즈니스 용어를 클래스/메서드명으로 변환
- Entity/Value Object 구분: 식별자 유무에 따른 객체 분류
- 메서드 시그니처 정의: 비즈니스 행동을 메서드로 구체화
언어 구체화 실무 예시:
- 비즈니스 용어 → 도메인 객체 변환:
|
|
- Value Object 설계 예시:
|
|
3 단계: Context Map → Integration Pattern → Domain Event 설계
핵심 활동:
- Context Map 해석: 컨텍스트 간 관계 분석
- Integration Pattern 선택: 적절한 통합 방식 결정
- Domain Event 설계: 비동기 통신 이벤트 정의
컨텍스트 통합 패턴 예시
- Context 관계 매핑:
|
|
- Domain Event 설계 예시:
|
|
4 단계: Subdomain 분류 → Module 구조 → Package/Namespace 조직화
핵심 활동:
- Subdomain 분류 적용: Core/Supporting/Generic 우선순위 반영
- Module 구조 설계: 패키지/네임스페이스 조직화
- 코드 구조 정의: 디렉터리 및 파일 구조 결정
모듈 구조 실무 예시:
- Subdomain 분류와 Module 매핑:
|
|
- 패키지 구조 상세:
|
|
고급 변환 패턴
Event Storming 에서 Tactical Design 으로의 변환
Event Storming 결과 → Aggregate 설계:
graph LR subgraph "Event Storming 결과" E1[ProductViewed] E2[ProductAddedToCart] E3[CartCheckedOut] E4[PaymentProcessed] E5[OrderShipped] end subgraph "Tactical Design 변환" A1[Product Aggregate] A2[Cart Aggregate] A3[Order Aggregate] A4[Payment Aggregate] A5[Shipment Aggregate] end E1 --> A1 E2 --> A2 E3 --> A3 E4 --> A4 E5 --> A5
실제 코드 변환 예시
Strategic Design 결정사항:
- Product Catalog Context 에서 " 상품 " 은 진열과 검색에 중점
- Order Context 에서 " 상품 " 은 주문 항목과 가격에 중점
Tactical Design 구현:
|
|
변환 시 주의사항 및 모범 사례
체계적인 변환 과정을 통해 Strategic Design 의 비즈니스 인사이트가 Tactical Design 의 구체적인 코드 구조로 정확하게 번역되며, 유지보수 가능하고 진화 가능한 시스템을 구축할 수 있다.
언어 일관성 유지
Context 경계 명확화
|
|
이벤트 기반 통합
|
|
상향식 피드백 (Tactical → Strategic)
도메인 모델과 소프트웨어는 도메인 전문가와 사용자의 피드백을 바탕으로 지속적으로 개선된다. 이를 통해 비즈니스 도메인이 진화함에 따라 소프트웨어가 정확하고 관련성을 유지할 수 있다.
피드백 유형:
피드백 신호 | 발생 위치 | Strategic 영향 | 조치 방향 |
---|---|---|---|
Aggregate 비대화 | Entity/Value Object | Context 경계 재검토 | 컨텍스트 분할 |
복잡한 Domain Service | 여러 Aggregate 간 | Subdomain 재정의 | 새로운 Core Domain 추출 |
Performance 이슈 | Repository/Query | Context Map 수정 | 통합 패턴 변경 |
언어 불일치 | 코드 vs 비즈니스 | Ubiquitous Language 정제 | 용어 표준화 |
실무 피드백 패턴:
Tactical 발견 | Strategic 개선 | 실무 예시 |
---|---|---|
Aggregate 복잡성 | Context 경계 재검토 | 너무 큰 Aggregate 는 Context 분할 신호 |
Domain Event 패턴 | Context 관계 개선 | 이벤트 흐름을 통한 의존성 파악 |
Repository 복잡성 | Subdomain 재분류 | 데이터 접근 패턴을 통한 도메인 이해 |
언어 불일치 | Ubiquitous Language 정제 | 구현 과정에서 발견되는 용어 모호성 |
피드백의 트리거 조건:
- 복잡성 임계값 (Complexity Thresholds)
- 예시
- Aggregate 가 7 개 이상의 Entity 를 포함할 경우, 경계가 너무 넓을 수 있으므로 해당 Bounded Context 의 분할을 검토.
- 사이클 복잡도 (Cyclomatic Complexity) 가 15 를 초과하는 경우, 도메인 서비스로 로직을 분리하거나 리팩토링이 필요.
- Context 간 호출이 분당 100 회를 넘는다면, 과도한 결합을 의미할 수 있으므로 통합 방식 (예: ACL, 이벤트 기반 통신 등) 을 재검토.
- 도메인 용어의 불일치 비율이 30% 이상이라면, 공유 언어 (Ubiquitous Language) 정립을 위한 워크샵을 통해 용어 정비가 필요.
- 예시
- 성능 임계값 (Performance Thresholds)
예시:
지속적 개선 사이클
graph LR A[Strategic Design] --> B[Tactical Implementation] B --> C[실행 경험] C --> D[학습 및 발견] D --> A style A fill:#e1f5fe style B fill:#f3e5f5 style C fill:#e8f5e8 style D fill:#fff3e0
순방향 흐름 (Top-Down):
- 비즈니스 도메인 → 전략적 설계 → Bounded Context 정의 → 전술적 설계 적용 → 구현 계층 반영
피드백 흐름 (Bottom-Up):
- 전술적 설계에서의 학습과 발견 → 전략적 설계 개선 → 도메인 이해 심화
이러한 순환적 관계를 통해 DDD 는 단순한 설계 방법론을 넘어 지속적으로 진화하는 도메인 이해의 프레임워크가 된다.
협력적 학습 메커니즘 (Collaborative Learning)
DDD 에서 전략적 설계와 전술적 설계의 균형을 맞추기 위해서는 이해관계자와의 협력이 필수적이다. 개발자는 이해관계자와 긴밀히 협력하여 소프트웨어 시스템이 비즈니스 요구사항을 만족하고 비즈니스 목표와 일치하도록 해야 한다.
협력 패턴
graph LR DE[Domain Expert] --> WS[Workshop Sessions] WS --> ES[Event Storming] ES --> BC[Bounded Context] BC --> AG[Aggregate Design] AG --> FB[Feedback to Domain] FB --> DE subgraph "Learning Cycle" ES --> UL[Ubiquitous Language] UL --> CODE[Code Implementation] CODE --> REVIEW[Domain Review] REVIEW --> UL end
이 상호작용적 발견 과정은 이해관계자, 도메인 전문가, 개발자가 함께 작업하여 도메인 이벤트의 흐름, 원인, 효과를 시각화하고 도메인에 대한 공유된 이해를 촉진한다.
진화적 수렴 메커니즘 (Evolutionary Convergence)
Strategic 과 Tactical Design 은 수렴 진화 (Convergent Evolution) 패턴을 보인다. 서로 다른 출발점에서 시작하여 점차 일치된 도메인 모델로 수렴해간다.
수렴 지표:
- Language Consistency: 코드 명명과 비즈니스 용어의 일치도
- Model Stability: Aggregate 경계의 안정성
- Integration Efficiency: Context 간 통신의 효율성
- Business Alignment: 비즈니스 규칙과 코드 로직의 일치성
구현 기법
Strategic Design 구현 기법
기법명 | 정의 및 목적 | 구성 요소 | 실제 예시 |
---|---|---|---|
Event Storming | 도메인 이벤트 중심으로 프로세스를 시각화하여 도메인 흐름 이해 | 도메인 전문가, 개발자, 스티커, 타임라인, 이벤트 | 주문 생성 → 결제 완료 → 배송 시작 |
Context Mapping | Bounded Context 간 관계 및 통합 전략 설계 | Upstream/Downstream, ACL, Conformist 등 통합 패턴 | 주문 ↔ 결제: Customer/Supplier 관계 |
Domain Storytelling | 도메인 흐름을 사람 중심의 서사로 표현하여 업무 이해 공유 | 액터, 객체, 순차적 행동 흐름 | 창고 직원이 주문을 수령하고 재고를 조정 |
Subdomain 분류 | 도메인을 Core, Supporting, Generic 으로 구분해 중요도 및 리소스 결정 | 비즈니스 가치 기반 도메인 식별 및 분류 | 결제 = Core, 고객 지원 = Supporting |
Context Mapper DSL | Bounded Context 정의를 코드로 표현하여 명확한 의사소통 및 설계 가능 | DSL 기반 정의 파일, Context, Relation | contextmap.dsl 파일을 통한 설계 시각화 |
Tactical Design 구현 기법
기법명 | 정의 및 목적 | 구성 요소 | 실제 예시 |
---|---|---|---|
Aggregate Design | 일관성 유지 단위를 정의하고 복잡한 도메인 객체를 하나로 묶음 | Aggregate Root, Entity, Value Object | Order + OrderItems + Money 객체 |
Entity/ValueObject 구현 | 식별 가능/불가능 객체로 모델링하여 도메인 표현력 향상 | 불변성, 동등성 비교, 생성자 유효성 검증 | Customer 엔티티, Email 값 객체 |
Repository Pattern | 도메인 모델의 저장소 추상화를 통해 인프라와 도메인 코드 분리 | 인터페이스, 구현체, DB 연동 코드 | CustomerRepository / JpaCustomerRepository |
Domain Service | Entity 에 포함시키기 어려운 도메인 로직을 외부에서 처리 | Stateless 함수, 복합 도메인 계산 | 할인 정책 적용 서비스 |
Factory Pattern | 복잡한 Aggregate 생성을 캡슐화하여 생성 책임 분리 | 정적 팩토리 메서드, 빌더 패턴 | OrderFactory.createOrder() |
Specification Pattern | 도메인 조건 및 검증을 객체로 분리해 재사용 및 조합 가능 | isSatisfiedBy() , AND/OR/NOT 조합 | 특정 조건의 고객만 조회하는 쿼리 명세 |
Hexagonal Architecture | 도메인 중심 아키텍처로 외부 인터페이스와 내부 모델을 분리 | Adapter (Inbound/Outbound), Application, Domain | REST API ↔ Application ↔ Domain ↔ Repository |
테스트 전략 | 도메인 로직에 대한 단위/통합 테스트로 설계 품질 보장 | Unit Test, Integration Test, Mock 객체 | Entity 유효성 테스트, 도메인 서비스 테스트 |
분류 기준에 따른 종류 및 유형
분류 기준 | 유형 | 설명 |
---|---|---|
설계 수준 | Strategic DDD | 바운디드 컨텍스트, 컨텍스트 맵, 유비쿼터스 언어 등 도메인 구조 전략 중심 |
Tactical DDD | 엔티티, 값 객체, 애그리게이트, 도메인 서비스 등 구현 전술 중심 | |
도메인 역할 | Core Domain | 핵심 경쟁력 중심 비즈니스 로직이 집중된 영역 |
Supporting Domain | 핵심 도메인을 보완하고 지원하는 비즈니스 영역 | |
Generic Domain | 기술적으로 범용적인 공통 기능 제공 영역 (예: 인증, 결제 시스템 등) | |
컨텍스트 관계 | Shared Kernel | 둘 이상의 팀이 공통 모델을 공유하며 협업 (강한 정합성 요구) |
Customer–Supplier | 공급자 - 소비자 관계를 명시한 모델 공유 방식 | |
Conformist | 하위 팀이 상위 컨텍스트의 모델을 그대로 수용하여 일관성을 유지 | |
Anticorruption Layer (ACL) | 레거시/외부 시스템과의 충돌 방지를 위해 인터페이스 또는 어댑터 계층 도입 | |
아키텍처 스타일 | Layered Architecture | 프레젠테이션, 애플리케이션, 도메인, 인프라 계층으로 나뉜 전통적 계층형 구조 |
Hexagonal Architecture | 포트와 어댑터 기반, 외부 시스템과 도메인을 분리하여 테스트 및 유지보수 용이화 | |
Clean Architecture | 의존성 규칙 (안쪽 → 바깥쪽) 적용, 도메인 중심의 아키텍처로 구조적 안정성 확보 | |
Event-Driven Architecture (EDA) | 도메인 이벤트 발행/구독 기반으로 컨텍스트 간 느슨한 결합 및 확장성 확보 | |
적용 범위 | 전체 시스템 적용 (Full DDD) | 모든 서브도메인 및 컨텍스트에 전략적/전술적 DDD 를 일관되게 적용 |
하이브리드 적용 (Hybrid DDD) | 복잡한 도메인만 DDD 적용, 나머지는 CRUD 등 기존 방식 유지 | |
파일럿 적용 (Partial DDD) | 선택된 모듈 또는 프로젝트 단위로 선별적 도입 | |
통합 방식 | Event-based Integration | 도메인 이벤트 발행/소비 기반, 비동기적 통신 중심 |
Synchronous Call (REST/gRPC) | 즉시 응답을 위한 RESTful API 또는 gRPC 기반 통합 방식 | |
데이터 일관성 | Strong Consistency | 트랜잭션 경계 내 데이터 일관성 보장 (단일 컨텍스트에서 주로 사용) |
Eventual Consistency | Bounded Context 간 비동기 메시징 기반 일관성 유지 | |
조직/협업 방식 | 통합 모델 (Unified Model) | 모든 팀이 하나의 모델을 공유 (소규모 프로젝트에서 적합) |
협력 모델 (Collaborative Model) | 팀별로 별도 바운디드 컨텍스트 운영, 컨텍스트 맵 기반 인터페이스 정의 | |
도입 전략 | Big Bang Adoption | 시스템 전반에 일괄 도입 (리스크와 난이도 큼) |
Incremental Adoption | 핵심 도메인부터 점진적 도입, 실험적 적용부터 확장 |
실무 사용 예시
산업 도메인별 DDD 적용 사례
산업 분야 | 활용 목적 | 적용 기술/패턴 | 기대 효과 |
---|---|---|---|
전자상거래 | 주문/결제/배송 도메인 모델링 및 경계 분리 | Aggregate, Bounded Context, CQRS, Event Sourcing | 독립적 배포, 복잡한 로직 캡슐화, 확장성 강화 |
금융 서비스 | 대출 승인, 리스크 분석, 트랜잭션 감사 | Domain Service, Entity, Kafka, Saga Pattern | 규제 대응 유연성, 감사 로그 확보, 도메인 로직 명확화 |
헬스케어 | 환자 기록 및 진료, 청구 시스템 구현 | Patient Aggregate, HL7/FHIR, Event Sourcing | 민감 데이터 무결성, 의료 로직 충실 구현, 표준화 |
물류/배송 | 배송 추적, 재고 시스템 최적화 | Shipment Aggregate, IoT Event, Event Storming | 실시간 추적, 물류 규칙 반영, 트래킹 통합 |
보험 | 클레임/언더라이팅/상품 설계 자동화 | Ubiquitous Language, External API, AI/ML | 정책 변경 민첩성, 자동 의사결정, 도메인 규칙 모델링 |
제조업 | 생산, 품질, 공급망의 통합 관리 | Aggregate, ERP 통합, Bounded Context + IoT | 생산 계획 최적화, 공정 통합, 품질 추적성 확보 |
실시간 시스템 | 배송 추적, 메시징, 스트리밍 서비스 설계 | CQRS, Event Sourcing, Kafka Streams | 실시간 처리, 복원력, 재처리 가능성 확보 |
이커머스 플랫폼 | 상품/주문/결제/배송 도메인 분리 및 API 설계 | Microservices, RESTful API, Bounded Context | 유연한 변경 대응, API 경계 명확화, 독립 서비스 운영 |
드론 배송 | 주문 - 배송 분산 처리 시스템 설계 | Domain Analysis, Aggregate, Event Storming | 경계 명확화, 분산 트랜잭션 설계 최적화 |
시스템 아키텍처/설계 적용 사례
컨텍스트 | 활용 목적 | 적용 기술/기법 | 효과 |
---|---|---|---|
이벤트 기반 마이크로서비스 설계 | Subdomain 식별 → MS 분리 → Aggregate 구현 | Context Map, Event Storming, CQRS, Event Sourcing | 각 서비스의 경계 명확화, 독립 배포 및 책임 분리 |
레거시 현대화 | 기존 시스템 점진적 전환 | Strangler Pattern, Anti-Corruption Layer | 레거시 기능 보존, 점진적 마이그레이션, 위험 최소화 |
엔터프라이즈 시스템 설계 | 복잡한 도메인 구조 대응 | Hexagonal Architecture, Clean Architecture | 비즈니스 로직 명확화, 유지보수 용이성 향상 |
API 설계 최적화 | 도메인 중심 API 계약 기반 구조화 | OpenAPI, GraphQL, API Gateway, Ubiquitous Language | 일관성 있는 API, 클라이언트 이해도 향상, 재사용성 증가 |
조직 및 협업/커뮤니케이션 중심 활용
활용 맥락 | 활용 목적 | 기법/도구 | 기대 효과 |
---|---|---|---|
팀 협업 및 소통 개선 | 도메인 전문가 ↔ 개발자 간 모델 정렬 | Ubiquitous Language, Domain Storytelling, Team Topologies | 커뮤니케이션 오류 최소화, 협업 효율성 향상 |
도메인 분석/경계 정의 | 하위 도메인 및 컨텍스트 구분 | Event Storming, Context Mapping DSL | 명확한 시스템 구조화, 독립적 책임 부여 가능 |
학습 곡선 및 문화 확산 | 초기 팀 도입/적용의 진입장벽 완화 | DDD-Lite, 시나리오 기반 교육, Pilot 프로젝트 | 전사적 확산 전 단계적 도입, 조직 문화 내재화 가능 |
구현 및 테스트 관점 실무 활용
사용 목적 | 적용 기술/전술 | 주요 효과 |
---|---|---|
도메인 모델 구현 | Entity, Value Object, Aggregate, Repository | 책임 분리 명확, 테스트 용이성 향상 |
통합 및 시나리오 테스트 | Consumer-driven Contract Test, E2E Test, Pact | Context 간 통합 보장, 회귀 오류 최소화 |
분산 트랜잭션 처리 | SAGA Pattern, Eventual Consistency, Compensation Logic | 강한 일관성 없이 분산 상태 제어, 시스템 복원력 강화 |
활용 사례
사례 1: 글로벌 이커머스 플랫폼의 주문 - 결제 - 배송 도메인
시스템 구성:
- 주문, 결제, 배송 각각 별도의 바운디드 컨텍스트
- 각 컨텍스트 내에 엔티티, 값 객체, 애그리게이트, 도메인 서비스, 이벤트, 리포지토리 존재
구조 다이어그램
graph TD subgraph 주문 컨텍스트 OrderEntity OrderAggregate OrderService OrderEvent OrderRepository end subgraph 결제 컨텍스트 PaymentEntity PaymentAggregate PaymentService PaymentEvent PaymentRepository end subgraph 배송 컨텍스트 DeliveryEntity DeliveryAggregate DeliveryService DeliveryEvent DeliveryRepository end OrderEvent -.-> PaymentService PaymentEvent -.-> DeliveryService
Workflow:
- 주문 생성 → 주문 이벤트 발생
- 결제 컨텍스트에서 주문 이벤트 수신 후 결제 처리
- 결제 성공 시 결제 이벤트 발생
- 배송 컨텍스트에서 결제 이벤트 수신 후 배송 처리
역할:
- 도메인 전문가: 요구사항 정의, 유비쿼터스 언어 정립
- 개발자: 도메인 모델 설계/구현
- QA: 도메인 이벤트 기반 테스트
사례 2: 카카오페이 여신코어 시스템 구축
시스템 구성
- 카카오페이는 복잡한 여신 업무를 위해 DDD 를 적용하여 시스템을 구축.
시스템 구성도:
graph TB subgraph "프레젠테이션 계층" A[여신 관리 API] B[대출 신청 API] C[심사 결과 API] end subgraph "응용 계층" D[여신 신청 서비스] E[심사 처리 서비스] F[납부 관리 서비스] end subgraph "도메인 계층" G[대출 애그리거트] H[고객 애그리거트] I[심사 애그리거트] J[납부 애그리거트] end subgraph "인프라 계층" K[여신 DB] L[외부 신용평가 API] M[알림 시스템] end A --> D B --> D C --> E D --> G E --> I F --> J G --> K I --> L J --> M
Workflow:
sequenceDiagram participant C as 고객 participant API as 여신 API participant APP as 응용 서비스 participant LOAN as 대출 애그리거트 participant REVIEW as 심사 애그리거트 participant EXT as 외부 신용평가 C->>API: 대출 신청 API->>APP: 대출 신청 처리 APP->>LOAN: 대출 생성 LOAN->>LOAN: 신청 정보 검증 LOAN-->>APP: 대출 신청 완료 이벤트 APP->>REVIEW: 심사 시작 REVIEW->>EXT: 신용 정보 조회 EXT-->>REVIEW: 신용 정보 응답 REVIEW->>REVIEW: 심사 규칙 적용 REVIEW-->>APP: 심사 완료 이벤트 APP->>LOAN: 심사 결과 반영 API-->>C: 대출 승인 결과
DDD 역할 및 효과:
- 유비쿼터스 언어 정립: 금융 도메인의 복잡한 용어를 개발자와 금융 전문가가 공통으로 사용
- 바운디드 컨텍스트 분리: 대출, 심사, 납부 등 각 영역의 독립적 발전 가능
- 애그리거트 설계: 복잡한 여신 규칙을 대출 애그리거트 내에서 일관성 있게 관리
- 도메인 이벤트 활용: 비동기 처리를 통한 시스템 성능 향상
기존 시스템과의 차이점:
- 기존: 절차 지향적 처리, 데이터베이스 중심 설계
- DDD 적용 후: 도메인 중심 설계, 비즈니스 로직의 명확한 표현, 변화에 유연한 대응
사례 3: 온라인 음식 배달 플랫폼
시스템 구성:
graph TB subgraph "Customer Management Context" A[Customer] B[Customer Profile] C[Loyalty Program] end subgraph "Restaurant Management Context" D[Restaurant] E[Menu] F[Restaurant Profile] end subgraph "Order Management Context" G[Order] H[Order Item] I[Payment] end subgraph "Delivery Context" J[Delivery] K[Driver] L[Route] end A --> G D --> G G --> J K --> J
Workflow:
sequenceDiagram participant C as Customer participant O as Order Service participant R as Restaurant participant P as Payment Service participant D as Delivery Service C->>O: 주문 생성 O->>R: 주문 확인 요청 R->>O: 주문 승인 O->>P: 결제 처리 P->>O: 결제 완료 O->>D: 배달 요청 D->>C: 배달원 배정 알림 D->>C: 배달 완료
DDD 역할 담당:
Strategic Design:
- Bounded Context 식별 (고객관리, 주문관리, 배달관리)
- Context Map 을 통한 통합 지점 정의
Tactical Design:
- Order Aggregate: 주문 일관성 보장
- Domain Events: OrderPlaced, PaymentCompleted, DeliveryStarted
- Repository: 각 Aggregate 에 대한 영속성 관리
Ubiquitous Language: " 주문 “, " 배달 “, " 음식점 " 등 비즈니스 용어 코드에 반영
사례 4: 헬스케어 조직–환자 관리 시스템
목적: 환자 관리 (Patient Management) 를 별도의 Bounded Context 로 분리하여 개발 독립성 확보
시스템 구성:
graph LR PatientB(ctx:환자관리) --> BillingB(ctx:청구) PatientB --> AppointmentB(ctx:예약)
구성요소:
- Strategic: Bounded Context 분리, Context Map 작성, Ubiquitous Language 정의
- Tactical: 환자 Entity, 주소 ValueObject, Repository, DomainService 구현
Workflow:
- 워크숍 통해 환자 - 주소 - 진료 이벤트 도출 (EventStorming)
- 환자 컨텍스트에서 Entity/VO 구현
- 청구/예약 컨텍스트와 OCR/ACL 방식으로 통합
- 독립 서비스로 배포 및 이벤트 기반 연동 설계
사례 5: 전자상거래 시스템 사례
시스템 구성:
- 대형 온라인 쇼핑몰의 도메인 주도 설계 적용 사례로, 다음과 같은 컨텍스트들로 구성되었다:
- 주문 관리 컨텍스트 (Order Management)
- 상품 카탈로그 컨텍스트 (Product Catalog)
- 재고 관리 컨텍스트 (Inventory Management)
- 결제 처리 컨텍스트 (Payment Processing)
- 배송 관리 컨텍스트 (Shipping Management)
시스템 구성 다이어그램
graph TB %% Strategic Design - Context Map subgraph "Strategic Design - Context Map" OC[Order Context] PC[Product Catalog Context] IC[Inventory Context] PayC[Payment Context] SC[Shipping Context] OC -->|Customer/Supplier| PC OC -->|Customer/Supplier| IC OC -->|Customer/Supplier| PayC OC -->|Customer/Supplier| SC PC -->|Shared Kernel| IC end %% Tactical Design - Order Context subgraph "Tactical Design - Order Context" OR[Order Aggregate Root] OI[Order Item Entity] Addr[Address Value Object] Money[Money Value Object] OR --> OI:::composition OR --> Addr:::composition OI --> Money:::composition end %% Style for composition classDef composition stroke-width:2px,stroke-dasharray: 5 5
Workflow 다이어그램
sequenceDiagram participant Customer participant OrderService participant ProductCatalog participant Inventory participant Payment participant Shipping Customer->>OrderService: 주문 생성 요청 OrderService->>ProductCatalog: 상품 정보 조회 ProductCatalog-->>OrderService: 상품 정보 반환 OrderService->>Inventory: 재고 확인 Inventory-->>OrderService: 재고 상태 반환 OrderService->>Payment: 결제 처리 요청 Payment-->>OrderService: 결제 완료 확인 OrderService->>Shipping: 배송 요청 OrderService-->>Customer: 주문 완료 응답
각 설계 접근법의 역할:
- Strategic Design 의 역할:
- 비즈니스 도메인을 5 개의 명확한 경계 컨텍스트로 분리
- 각 컨텍스트 간의 관계를 Customer/Supplier, Shared Kernel 패턴으로 정의
- 팀 조직을 컨텍스트 경계에 맞춰 구성 (Conway’s Law 적용)
- Tactical Design 의 역할:
- Order Aggregate 를 통한 주문 일관성 보장
- Value Object (Money, Address) 를 통한 불변성 확보
- Repository 패턴을 통한 데이터 접근 추상화
차이점 비교
관점 | Strategic Design 적용 | Tactical Design 적용 |
---|---|---|
팀 구조 | 컨텍스트별 독립적인 팀 운영 | 단일 팀 내 역할 분담 |
데이터베이스 | 컨텍스트별 독립적인 데이터 저장소 | 애그리게이트별 테이블 설계 |
API 설계 | 컨텍스트 간 명확한 계약 정의 | 도메인 객체 기반 인터페이스 |
변경 영향도 | 컨텍스트 경계 내로 제한 | 애그리게이트 경계 내로 제한 |
구현 예시
온라인 쇼핑몰 주문 처리 시스템 (Python)
|
|
도메인 계층
구성 요소 | 유형 | 핵심 역할 |
---|---|---|
Order | Aggregate Root | 주문 생명주기 관리 및 비즈니스 규칙 강제 |
OrderItem | Entity | 개별 상품 주문 정보 관리 |
Money, CustomerId, Address | Value Objects | 도메인 개념의 타입 안전성 보장 |
OrderStatus | Enumeration | 주문 상태의 명확한 정의 |
DomainEvent | Event | 도메인에서 발생하는 중요 사건 표현 |
OrderDomainService | Domain Service | 복잡한 도메인 로직 처리 |
OrderRepository | Repository Interface | 영속성 추상화 |
classDiagram direction TB %% Aggregate Root class Order { <<Aggregate Root>> - id: UUID - customerId: CustomerId - shippingAddress: Address - status: OrderStatus - createdAt: datetime - items: List~OrderItem~ - _domainEvents: List~DomainEvent~ + addItem(productId: UUID, quantity: int, unitPrice: Money) + removeItem(productId: UUID) + calculateTotal(): Money + confirmOrder() + shipOrder() + getDomainEvents(): List~DomainEvent~ + clearDomainEvents() - _findItem(productId: UUID): OrderItem - _addDomainEvent(event: DomainEvent) } %% Entities class OrderItem { <<Entity>> - productId: UUID - quantity: int - unitPrice: Money + calculateTotal(): Money + changeQuantity(newQuantity: int) } %% Value Objects class Money { <<Value Object>> - amount: Decimal - currency: string + add(other: Money): Money + multiply(multiplier: int): Money } class CustomerId { <<Value Object>> - value: UUID + generate(): CustomerId } class Address { <<Value Object>> - street: string - city: string - postalCode: string - country: string } %% Enumerations class OrderStatus { <<enumeration>> DRAFT CONFIRMED PAID SHIPPED DELIVERED CANCELLED } %% Domain Events class DomainEvent { <<Abstract>> - eventId: UUID - occurredAt: datetime - aggregateId: UUID } class OrderCreated { <<Domain Event>> - customerId: UUID - totalAmount: Money } class OrderItemAdded { <<Domain Event>> - productId: UUID - quantity: int - price: Money } %% Domain Service class OrderDomainService { <<Domain Service>> + canApplyDiscount(order: Order, discountPercentage: Decimal): bool + calculateShippingFee(order: Order): Money } %% Repository Interface class OrderRepository { <<interface>> + save(order: Order): void + findById(orderId: UUID): Order + findByCustomerId(customerId: CustomerId): List~Order~ } %% 관계 설정 Order --> "1..*" OrderItem : aggregates Order --> "1" CustomerId : contains Order --> "1" Address : contains Order --> "1" OrderStatus : has Order --> "*" DomainEvent : publishes OrderItem --> "1" Money : uses DomainEvent <|-- OrderCreated : extends DomainEvent <|-- OrderItemAdded : extends OrderDomainService ..> Order : operates on OrderRepository ..> Order : manages
응용/프레젠테이션/인프라 계층
구성 요소 | 유형 | 핵심 역할 |
---|---|---|
OrderController | Presentation | HTTP 요청/응답 처리 |
OrderApplicationService | Application | 유스케이스 조정 및 트랜잭션 관리 |
Command Objects | Application | 요청 데이터 캡슐화 |
InMemoryOrderRepository | Infrastructure | 실제 저장소 구현체 |
classDiagram direction TB %% Presentation Layer class OrderController { <<Controller>> - orderService: OrderApplicationService + createOrder(requestData: dict): dict } %% Application Layer class OrderApplicationService { <<Application Service>> - orderRepository: OrderRepository + createOrder(command: CreateOrderCommand): UUID + addOrderItem(command: AddOrderItemCommand): void + confirmOrder(orderId: UUID): void } %% Commands (Application Layer) class CreateOrderCommand { <<Command>> - customerId: UUID - shippingAddress: Address - items: List~dict~ } class AddOrderItemCommand { <<Command>> - orderId: UUID - productId: UUID - quantity: int - unitPrice: Decimal } %% Infrastructure Layer class InMemoryOrderRepository { <<Repository Implementation>> - _orders: dict~UUID, Order~ + save(order: Order): void + findById(orderId: UUID): Order + findByCustomerId(customerId: CustomerId): List~Order~ } %% Repository Interface (from Domain) class OrderRepository { <<interface>> + save(order: Order): void + findById(orderId: UUID): Order + findByCustomerId(customerId: CustomerId): List~Order~ } %% Domain Reference (for context) class Order { <<Aggregate Root>> - id: UUID - customerId: CustomerId - status: OrderStatus + confirmOrder() + addItem() + calculateTotal(): Money } class Address { <<Value Object>> - street: string - city: string - postalCode: string - country: string } %% 관계 설정 OrderController --> OrderApplicationService : uses OrderApplicationService --> OrderRepository : depends on OrderApplicationService --> CreateOrderCommand : processes OrderApplicationService --> AddOrderItemCommand : processes OrderRepository <|.. InMemoryOrderRepository : implements CreateOrderCommand --> Address : contains OrderApplicationService ..> Order : creates and manages InMemoryOrderRepository --> Order : stores
데이터 플로우
- 요청 처리:
OrderController
→OrderApplicationService
- 커맨드 변환: HTTP Request → Command Object
- 도메인 로직 실행: Application Service → Domain Objects
- 데이터 영속화: Application Service → Repository
- 응답 생성: Domain Objects → DTO → HTTP Response
추가 개선 및 확장 가능 부분
결제 (Payment) 도메인 추가
classDiagram class Payment { - paymentId: UUID - orderId: UUID - amount: Money - paymentMethod: PaymentMethod - status: PaymentStatus - processedAt: datetime + process() + refund(amount: Money) + cancel() } class PaymentMethod { <<enumeration>> CREDIT_CARD BANK_TRANSFER KAKAO_PAY NAVER_PAY } class PaymentStatus { <<enumeration>> PENDING COMPLETED FAILED REFUNDED CANCELLED } Payment --> PaymentMethod Payment --> PaymentStatus Payment --> Money
재고 (Inventory) 도메인 추가
classDiagram class Product { - productId: UUID - name: string - price: Money - inventory: Inventory + updatePrice(newPrice: Money) + isAvailable(quantity: int): bool } class Inventory { - productId: UUID - currentStock: int - reservedStock: int - minimumStock: int + reserve(quantity: int) + release(quantity: int) + restock(quantity: int) + getAvailableStock(): int } Product --> Inventory Product --> Money
이벤트 소싱 (Event Sourcing) 패턴 적용
구성 요소 | 설명 |
---|---|
EventStore | 도메인 이벤트를 순서대로 저장하는 저장소 |
EventStreamReader | 이벤트 스트림을 읽어 애그리거트 상태를 복원 |
EventBus | 이벤트를 다른 바운디드 컨텍스트로 전파 |
Snapshot | 성능 최적화를 위한 애그리거트 스냅샷 |
CQRS (Command Query Responsibility Segregation) 적용
classDiagram %% 커맨드 사이드 class OrderCommandHandler { + handle(CreateOrderCommand) + handle(AddOrderItemCommand) + handle(ConfirmOrderCommand) } %% 쿼리 사이드 class OrderQueryHandler { + handle(GetOrderByIdQuery): OrderDto + handle(GetOrdersByCustomerQuery): List~OrderDto~ + handle(GetOrderSummaryQuery): OrderSummaryDto } class OrderDto { - orderId: string - customerName: string - totalAmount: decimal - status: string - createdAt: datetime } class OrderSummaryDto { - totalOrders: int - totalRevenue: decimal - averageOrderValue: decimal } OrderQueryHandler --> OrderDto OrderQueryHandler --> OrderSummaryDto
도메인 검증 강화
검증 유형 | 구현 위치 | 예시 |
---|---|---|
불변 조건 (Invariants) | Entity/Value Object | 주문 총액이 0 보다 커야 함 |
전제 조건 (Preconditions) | 메서드 시작 부분 | 주문 상태가 DRAFT 일 때만 항목 추가 가능 |
후행 조건 (Postconditions) | 메서드 종료 부분 | 주문 확정 후 상태가 CONFIRMED 인지 확인 |
사업 규칙 (Business Rules) | Domain Service | 할인율은 최대 30% 까지만 적용 가능 |
성능 최적화 패턴
classDiagram class OrderProjection { - orderId: UUID - customerName: string - totalAmount: decimal - itemCount: int - lastUpdated: datetime + updateFromEvent(event: DomainEvent) } class OrderCache { - cache: Map~UUID, OrderProjection~ + get(orderId: UUID): OrderProjection + put(orderId: UUID, projection: OrderProjection) + invalidate(orderId: UUID) } class OrderStatistics { - totalOrders: long - totalRevenue: Money - averageOrderValue: Money + updateFromOrderCreated(event: OrderCreated) + updateFromOrderCancelled(event: OrderCancelled) } OrderCache --> OrderProjection
보안 및 권한 관리
보안 요소 | 설명 | 구현 방법 |
---|---|---|
인증 (Authentication) | 사용자 신원 확인 | JWT 토큰, OAuth 2.0 |
인가 (Authorization) | 권한 기반 접근 제어 | RBAC (Role-Based Access Control) |
데이터 암호화 | 민감 정보 보호 | 개인정보, 결제 정보 암호화 |
감사 로그 | 중요 작업 기록 | 주문 생성, 수정, 취소 이력 추적 |
모니터링 및 관찰 가능성
|
|
이러한 확장을 통해 더욱 견고하고 확장 가능한 도메인 주도 설계 기반 시스템을 구축할 수 있다. 각 패턴과 구성 요소는 시스템의 복잡성과 요구사항에 따라 단계적으로 도입하는 것을 권장한다.
장점
분류 | 항목 | 설명 |
---|---|---|
설계 중심 | 비즈니스 중심 설계 | 도메인 모델이 비즈니스 로직을 정확히 반영하여 소프트웨어와 비즈니스의 정렬성 향상 |
커뮤니케이션 | 유비쿼터스 언어 | 도메인 전문가와 개발자가 공유하는 공통 언어로 일관된 커뮤니케이션 유지 |
구조화 | 바운디드 컨텍스트 | 도메인 경계를 명확히 구분하여 복잡성 분리 및 모듈화 가능 |
유지보수성 | 높은 응집도 / 낮은 결합도 | 관련 도메인 로직의 집중화 및 컨텍스트 간 최소 의존성으로 변경 용이 |
테스트 용이성 | 테스트 편의성 | 도메인 로직이 독립적으로 유지되어 단위 테스트 및 유지보수 간편 |
확장성 | 독립적 배포 및 진화 구조 | 마이크로서비스 및 독립 배포에 적합한 구조로, 확장성과 팀 분산 개발에 유리 |
복잡성 관리 | 복잡한 비즈니스 모델 캡슐화 | 핵심 도메인을 중심으로 복잡한 로직을 체계적으로 정리하여 유지보수성 및 설계 품질 향상 |
DevOps 연계 | 이벤트 기반 구조 연동 가능 | 이벤트 흐름 명시화를 통한 감사 로그, 분석 로깅, DevOps 기반 모니터링에 유리 |
단점 및 문제점
단점
카테고리 | 항목 | 설명 | 권장 대응 전략 |
---|---|---|---|
학습/적용 난이도 | 높은 러닝 커브 | DDD 개념 (BC, Aggregate, VO 등) 과 패턴 학습에 시간과 경험 필요 | 점진적 도입, 사내 교육 운영, 멘토링 체계 구축 |
설계 복잡성 | 과도한 추상화 | 단순 CRUD 시스템에도 불필요한 설계 적용 시 오버엔지니어링 우려 | 복잡성 분석 후 핵심 도메인만 적용 (하이브리드 구조 병행) |
초기 개발 부담 | 높은 설계 및 협업 비용 | 바운디드 컨텍스트 식별, 모델링, 유비쿼터스 언어 설계 등에 초기 시간 요구 | Event Storming, 프로토타이핑, Evolutionary Design 도입 |
조직 문화 요건 | 도메인 전문가 협업 의존 | 도메인 전문가와 지속적인 협업이 필수지만 현실적으로 확보 어려움 | 도메인 지식 문서화, 전문가 세션 정기화, 팀 내 도메인 챔피언 지정 |
운영 복잡도 | CI/CD, 통합 테스트 부담 | 바운디드 컨텍스트별 배포, 계약 테스트, 이벤트 흐름 테스트 등으로 DevOps 부담 증가 | GitOps, Contract Testing, CI 자동화 전략 적용 |
문제점
문제 유형 | 항목 | 원인 또는 진단 | 영향 | 해결 전략 / 대응 기법 |
---|---|---|---|---|
모델링 오류 | Anemic Domain Model | 로직이 서비스 계층에 집중, Entity 가 데이터만 보유 | 도메인 로직 분산, 캡슐화 부족 | Entity 내부에 비즈니스 로직 포함, Rich Domain Model 설계 |
모델 경계 오류 | Bounded Context 경계 불명확 | 컨텍스트 간 책임 불분명, 용어 혼용 | 시스템 일관성 저하, 충돌 발생 | Context Map 작성, 용어 기준 경계 설정, 이벤트 스토밍으로 언어 경계 정의 |
오용 패턴 | CQRS / Event Sourcing 과도 도입 | 단순 기능에도 복잡한 아키텍처 적용 | 성능 저하, 유지보수 어려움 | 도메인 복잡도 기준 적용 여부 판단, 전략적 패턴 분리 적용 |
설계 비대화 | 도메인 서비스 오남용 | 모든 비즈니스 로직을 서비스에 몰아넣음 | 도메인 객체의 책임 약화 | Entity 중심 설계로 책임 재조정, 서비스 로직 분산 |
통합 문제 | 레거시 통합 복잡도 | 기존 시스템과 모델/계약 불일치 | 마이그레이션 실패, 서비스 중단 위험 | Anti-Corruption Layer, Strangler Pattern 단계적 도입 |
이벤트 과다 사용 | 도메인 이벤트 오남용 | 명확한 목적 없는 이벤트 남발 | 추적 어려움, 순환 참조 위험 | 명확한 이벤트 설계 가이드 수립, 이벤트 체계 문서화 |
데이터 일관성 | Aggregate 경계 오설정 | 동일 트랜잭션에 다수 Aggregate 포함 | 트랜잭션 실패, 성능 저하 | 경계 재설정, SAGA, Eventual Consistency 기반 설계 |
성능 저하 | 리포지토리 과사용 / 쿼리 병목 | 복잡한 비즈니스 로직이 리포지토리에 집중 | 성능 병목, 테스트 어려움 | CQRS 도입, Specification Pattern, Projection 분리 |
Anemic Domain Model 문제
원인: 비즈니스 로직이 Service Layer 에 집중되어 Domain Object 가 데이터만 담는 현상
영향: 도메인 모델의 캡슐화 파괴, 비즈니스 로직 분산
탐지 및 진단: Entity 와 Value Object 에 getter/setter 만 존재하는지 검사
예방 방법: Entity 와 Value Object 에 비즈니스 로직 포함, Tell Don’t Ask 원칙 적용
해결 방법:
|
|
Repository 패턴 오용
원인: Repository 를 단순 DAO 로 사용하거나 비즈니스 로직 포함
영향: 관심사 분리 원칙 위배, 테스트 어려움
탐지 및 진단: Repository 에 비즈니스 로직이나 복잡한 쿼리 로직 존재 여부 확인
예방 방법: Repository 인터페이스를 도메인 관점에서 정의
해결 방법:
|
|
도전 과제 및 해결책
조직 및 협업 과제
도전 과제 | 원인 | 영향 | 예방 전략 | 해결 방법 |
---|---|---|---|---|
도메인 전문가 협업 부족 | 시간 제약, 용어 차이 | 정보 단절, 설계 오류 | 정기적 이벤트 스토밍, 유비쿼터스 언어 정착 | 크로스 펑셔널 팀 구성, 페어 설계 |
DDD 도입 저항 | 기존 관행, 학습 부담 | 도입 지연, 낮은 품질 | 교육 세션, 점진적 도입 | 성공 사례 공유, 실험적 적용 |
팀 간 도메인 이해 격차 | 용어 정의 불일치 | 소통 오류 | 용어사전 작성, 모델 리뷰 | 코드 리뷰 시 도메인 개념 반영 |
조직과 컨텍스트 불일치 | 조직 구조와 도메인 불일치 | Context Drift | Team Topologies 반영 | 팀 구조/컨텍스트 재정렬 |
설계 및 모델링 과제
도전 과제 | 원인 | 영향 | 예방 전략 | 해결 방법 |
---|---|---|---|---|
컨텍스트 경계 설정 어려움 | 개념적 추상성 | 복잡도 증가 | 이벤트 스토밍, 용어 기반 구분 | 조직 구조 연계한 Context Map 설계 |
도메인 모델 설계 난이도 | 복잡한 비즈니스 로직 | 추상화 실패 | 핵심 도메인부터 모델링 | 반복적 리팩토링 및 모델링 세션 활용 |
유비쿼터스 언어 유지 어려움 | 문서화 부족 | 소통 오류 | 코드 기반 언어 사용, 정기 워크숍 | 정기 리뷰 및 자동화 도구 적용 |
기술 및 아키텍처 과제
도전 과제 | 원인 | 영향 | 예방 전략 | 해결 방법 |
---|---|---|---|---|
이벤트 아키텍처 복잡성 | 멱등성, 순서 문제 | 디버깅 어려움 | 이벤트 스키마 명세화 | 메시지 추적 도구, 멱등 처리 적용 |
데이터 일관성 확보 | 트랜잭션 경계 넘는 연산 | 일관성 오류 | Saga, Outbox, 보상 트랜잭션 | 분산 트레이싱 기반 설계 |
CQRS/Event Sourcing 과도 적용 | 모든 도메인에 적용 | 설계 오버헤드 | 복잡도 기준 선별 도입 | Hybrid CQRS 전략 적용 |
이벤트/모델 버전 관리 문제 | 계약 변경 시 장애 | 서비스 중단 | 명세 기반 계약 테스트 | Event Versioning, Schema Registry |
설계 문서 부족 | 코드 중심 개발 | 설계 의도 손실 | Living Docs 도입 | ADR, 자동화 문서 시스템 활용 |
레거시 통합 과제
도전 과제 | 원인 | 영향 | 예방 전략 | 해결 방법 |
---|---|---|---|---|
마이그레이션 복잡성 | 기술 부채, 데이터 종속 | 전환 비용 증가 | 점진적 전환, ACL 활용 | Strangler Fig 패턴, 리팩터링 적용 |
데이터 정합성 문제 | 컨텍스트 간 중복 저장 | 불일치 및 오류 | 이벤트 기반 통합 | 동기 - 비동기 분리, Context Mapping 활용 |
운영 및 테스트 과제
도전 과제 | 원인 | 영향 | 예방 전략 | 해결 방법 |
---|---|---|---|---|
이벤트 호환성 문제 | 이벤트 변경 | 소비자 장애 | 명시적 버전 관리 | 하위 호환 이벤트 설계, API Gateway 활용 |
모니터링/추적 부재 | 비동기 구조 | 장애 진단 어려움 | 도메인 이벤트 기반 KPI | Distributed Tracing, 이벤트 로깅 |
테스트 어려움 | 비동기 흐름, 순서 | 테스트 신뢰도 저하 | Contract Test 도입 | Replay 기반 통합 테스트 구성 |
비즈니스/외부 환경 과제
도전 과제 | 원인 | 영향 | 예방 전략 | 해결 방법 |
---|---|---|---|---|
시장 변화 대응 어려움 | 설계 고정화 | 민첩성 저하 | 느슨한 결합, 기능 토글 | CD 기반 유연한 배포 |
규제 및 감사 추적 | 컴플라이언스 요구 | 감사/규제 대응 어려움 | 이벤트 기반 설계 | Event Sourcing, Projection 기반 감사 로그 구성 |
기술 트렌드 관련 과제
도전 과제 | 원인 | 영향 | 예방 전략 | 해결 방법 |
---|---|---|---|---|
마이크로서비스와 DDD 경계 불일치 | 컨텍스트 경계 모호 | 분산 복잡도 증가 | Context Map 기반 설계 | 이벤트 기반 아키텍처, SAGA 적용 |
클라우드 네이티브 적응 | 기존 설계 패턴과의 괴리 | 확장성 제약 | 클라우드 패턴 반영 | Serverless, 컨테이너 기반 설계 |
실무에서 효과적으로 적용하기 위한 고려사항 및 주의할 점
카테고리 | 항목 | 설명 | 권장사항 |
---|---|---|---|
도메인 협업 | 도메인 전문가 참여 확보 | 비즈니스 로직의 왜곡 방지를 위해 실제 도메인 지식을 갖춘 전문가 참여 필요 | Event Storming, 정기 설계 세션, 1:1 인터뷰 기반 협업 |
유비쿼터스 언어 정립 | 코드, 문서, 회의 등에서 일관된 도메인 용어 사용 | 용어 사전 구축, 도메인별 리뷰 체크리스트 활용 | |
설계 및 경계 정의 | 바운디드 컨텍스트 식별 | 도메인 기능별 명확한 경계 정의가 없을 경우 책임 불분명 | 컨텍스트 맵 작성, 조직 구조 및 팀 책임 기반 경계 설계 |
핵심 도메인 우선 적용 | 단순 기능 영역까지 과도한 적용 시 시간과 리소스 낭비 | 복잡도가 높은 핵심 영역부터 점진적으로 적용 | |
모델 단순화 | VO, Entity, Service 등 과도한 패턴 남용은 복잡성 증가 | 설계 복잡도 대비 비즈니스 가치 중심으로 전술적 패턴 선택 | |
기술/인프라 준비 | 이벤트 기반 통신 | REST 기반만 사용하는 경우 경계 간 통합 유연성이 낮음 | Kafka, RabbitMQ 등 메시징 기반 아키텍처 도입 |
Anti-Corruption Layer 설계 | 레거시 시스템과의 직접 통합은 도메인 오염 유발 | ACL 패턴 활용, 레거시 점진적 교체 전략 수립 | |
이벤트 소싱 및 CQRS 인프라 검토 | 고급 패턴 적용 시 기술 스택/운영 환경의 준비도 필요 | 스냅샷 주기, 별도 읽기 모델 DB 구성 등 인프라 사전 준비 | |
운영 및 배포 | 배포 전략 설계 | 각 컨텍스트가 독립 배포되지 않으면 변경 시 전체 서비스 영향 발생 | CI/CD 분리, Context 단위 배포 파이프라인 구성 |
데이터 일관성 보장 | 트랜잭션 범위가 컨텍스트를 넘으면 강한 일관성 보장 어려움 | Saga 패턴, Eventual Consistency 도입 | |
이벤트 버전 관리 | 스키마 변경 시 하위 호환성 문제 발생 가능 | 이벤트 명세 관리, 버전 명시, Message Contract 기반 테스트 적용 | |
테스트 및 품질 관리 | 단위 테스트 / 계약 테스트 구분 | 도메인 로직과 통합 흐름 모두를 검증할 수 있어야 실질적 신뢰 확보 가능 | 도메인 단위 유닛 테스트 + Bounded Context 간 계약 테스트 병행 |
통합 테스트 / 시나리오 테스트 | 이벤트 흐름, 상태 변경, 협업 통신 시나리오까지 포함한 검증 필요 | E2E 테스트, 컨텍스트 간 이벤트 Replay 기반 검증 | |
조직 및 문화 | 학습 문화 구축 | 설계 접근법 및 도구에 대한 팀 전체의 이해 부족 시 실패 확률 상승 | 사내 DDD 교육 프로그램, 피어 세션, 모델 리뷰 문화 내재화 |
크로스펑셔널 협업 체계 | 개발자 중심 설계는 실제 도메인 요구와 괴리 가능 | 도메인 전문가, QA, 기획자 포함된 설계 그룹 구성 | |
피드백 기반 설계 반복 | 초기 설계 고착화는 시스템 진화 저해 | 설계 → 구현 → 회고 → 리팩토링의 반복 가능한 사이클 운영 | |
도입 전략 및 리스크 관리 | 점진적 도입 | Big Bang 방식은 리스크, 비용, 실패 가능성 높음 | 핵심 도메인 중심 MVP 단위로 시작, 성공 사례를 통한 확산 |
복잡도 사전 평가 | 단순한 CRUD 시스템에는 오히려 비용 대비 효과 적음 | 도메인 복잡도/비즈니스 중요도 평가 후 선별 적용 | |
DDD 이해도 진단 | 팀 내 DDD 이해 수준 미흡 시 잘못된 구현/남용 우려 | 도입 전 DDD 교육, 설계 패턴/도구에 대한 공유 및 기준 문서화 |
Domain-Driven Design 도입 단계별 전략
DDD(Domain-Driven Design) 를 성공적으로 도입하려면 단계별 전략적 접근이 매우 중요하다.
도입 전 준비 → 설계 → 구현 → 운영 및 확산까지 포함한 DDD 도입 단계별 전략 로드맵으로, 각 단계는 현실적인 조직 환경과 기술 복잡성을 고려해 구성되어 있다.
단계 | 주요 목표 | 핵심 활동 | 체크포인트 |
---|---|---|---|
1. 준비 단계 | 조직/팀 준비도 평가 및 기반 마련 | - 도메인 복잡도/도입 필요성 분석 - 핵심 도메인 식별 - 팀의 DDD 이해도 확인 - 도메인 전문가 참여 확보 | 🔸 단순 CRUD 시스템인가? 🔸 도메인 전문가와의 협업 채널 존재 여부 |
2. 학습 및 훈련 단계 | DDD 개념과 접근법 내재화 | - DDD 핵심 개념 교육 - 전략적/전술적 설계 훈련 Event Storming 워크숍 실습 - 유비쿼터스 언어 정의 시작 | 🔸 팀 전체가 DDD 의 기본 개념을 공유하고 있는가? 🔸 코드/문서/언어 간 용어 일관성 확보 여부 |
3. 핵심 도메인 선정 및 점진적 적용 | MVP 수준의 핵심 도메인부터 적용 | - Core Domain 중심 바운디드 컨텍스트 설계 Aggregate, Entity, VO 정의 Domain Event 기반 통신 설계 ACL, CQRS, Saga 등 전략적 도입 | 🔸 시작 범위가 적절히 작고 명확한가? 🔸 이벤트 흐름이 모델로 설명 가능한가? |
4. 구현 및 기술 적용 단계 | 실제 구현을 통해 모델 검증 | - 애플리케이션/도메인/인프라 계층 분리 - 리포지토리, 도메인 서비스 구현 - 이벤트 소싱/CQRS 인프라 구축 - 테스트 전략 수립 (단위 + 계약 + 시나리오) | 🔸 도메인 로직이 도메인 모델에 집중되어 있는가? 🔸 이벤트 버저닝, 장애 보상 설계가 되어 있는가? |
5. 운영 및 통합 단계 | 실 운영 환경에서 안정적 서비스 유지 | - 분산 트레이싱 및 이벤트 로깅 - 이벤트 재처리 메커니즘 - 모니터링 + 도메인 지표 수집 API Gateway 및 메시지 브로커 연동 | 🔸 이벤트 흐름이 추적 가능한가? 🔸 장애 복구 또는 보상 시나리오가 설계되었는가? |
6. 확산 및 리팩토링 단계 | 모델의 지속적 개선 및 조직 전체 확산 | - 추가 도메인 적용 - 공통 컨텍스트 리팩토링 - 팀 구조와 컨텍스트 정렬 - 조직 차원의 도메인 문서화 문화 정착 | 🔸 DDD 모델이 조직 전반에 재사용 가능한 구조인가? 🔸 팀/서비스 구조가 도메인 중심으로 재편되었는가? |
보완 전략
보완 항목 | 전략적 대응 방법 |
---|---|
기술 부족 | 교육/사내 세미나, 코드랩 도입 |
협업 도구 부족 | Miro, Context Mapper, Event Storming Tool 도입 |
이벤트 통합의 어려움 | ACL, API Gateway, Kafka 전략 병행 |
유지보수 어려움 | 문서화 자동화, 테스트 커버리지 강화 |
최적화 고려사항 및 권장사항
카테고리 | 항목 | 설명 | 권장사항 |
---|---|---|---|
성능 최적화 | 애그리게이트 크기 조절 | 과도한 Aggregate 는 트랜잭션 충돌과 병렬성 저하 유발 | 불변성·일관성 기준 최소 단위로 설계, 필요한 경우 분할 |
CQRS 적용 | 명령/조회 책임 분리로 성능과 확장성 확보 | Projection 기반 Read Model, 읽기 전용 DB 활용 | |
이벤트 처리 최적화 | 이벤트 폭주 시 메시지 큐 병목 가능 | Kafka 파티셔닝, 비동기 배치 처리 | |
Selective Loading | 연관 객체 전부 로딩 시 성능 저하 | Lazy Loading, DTO/Projection 사용 | |
확장성 최적화 | 바운디드 컨텍스트 독립성 확보 | 컨텍스트 간 강한 결합은 유지보수 비용 증가 | 이벤트 기반 비동기 통신 구조 설계 |
데이터베이스 분리 | 단일 DB 는 스케일 아웃 제한 | Database per Service 또는 Polyglot Persistence 적용 | |
API 경계 명확화 | 모호한 API 는 재사용성과 안정성 저해 | Context 기반 API 설계, API Gateway 연계 | |
유지보수성 향상 | 유비쿼터스 언어 일관성 | 용어 혼용 시 개발자 - 도메인 전문가 간 오해 발생 | 언어 사전 관리, 코드/문서/회의 일관성 유지 |
설계 문서화 전략 | 설계 지식이 코드에만 존재하면 유지 및 전달 어려움 | ADR(Architecture Decision Record) 작성, Markdown 기반 기록 | |
반복 가능한 리팩토링 체계 | 초기 설계는 변경 가능성 존재 | 도메인 리뷰 주기화, 리팩토링 문화 내재화 | |
테스트 전략 | 단위 테스트 명확화 | 전술 객체 (Entity, VO 등) 에 대한 명확한 테스트 필요 | 책임 기반 설계, Fake 활용 테스트 우선 |
컨텍스트 간 통합 테스트 | 이벤트 누락·API 연계 오류 등 검출 어려움 | 시나리오 기반 E2E 테스트 구성, 소비자 계약 테스트 (Pact) 활용 | |
전략 모델 테스트 어려움 | 전략 구조는 구체 코드가 없어 테스트 설계 어려움 | BDD 기반 시나리오 테스트 작성 | |
데이터 일관성 | 트랜잭션 경계 명확화 | 다수 Aggregate 간 강한 일관성은 확장성 저해 | 내부는 강한 일관성, 외부는 SAGA + Eventual Consistency 사용 |
Event Sourcing 활용 | 상태 변경 이력 저장을 통한 추적 및 복구 가능 | Snapshot 전략 수립, 이벤트 리플레이 설계 | |
통합 및 마이그레이션 | Anti-Corruption Layer 적용 | 레거시 통합 시 도메인 오염 발생 가능 | ACL 통해 외부 시스템과 내부 도메인 분리 |
이벤트 기반 통신 구조 | 동기 API 기반 구조는 장애 전파 위험 | Kafka, RabbitMQ 등 메시징 기반 통합 적용 | |
Context 간 통합 전략 설계 | 잘못된 통합 방식은 결합도 증가와 유지보수 어려움 | Context Map 기반 관계 분석 후 전략 선택 (ACL, Conformist 등) | |
모니터링 및 가시성 | 도메인 이벤트 기반 메트릭 수집 | 기술 지표만으로는 비즈니스 흐름 파악 어려움 | KPI 중심 도메인 이벤트 추적 및 시각화 (Grafana, Prometheus 등) |
분산 추적 및 로깅 | 서비스 간 흐름 분석 부재 시 장애 분석 어려움 | OpenTelemetry, Jaeger, Zipkin 연동 | |
이벤트 흐름 가시성 | 메시지 흐름 누락/지연 식별 어려움 | Kafka Lag 모니터링, Dead Letter Queue 적용 | |
조직 및 협업 구조 | 도메인 전문가와 협업 강화 | 개발자 중심 설계 시 비즈니스와 괴리 발생 | Event Storming, Domain Storytelling 도입 |
조직 구조 ↔ 도메인 정렬 | 팀 구조와 도메인 경계 불일치 시 커뮤니케이션 비용 증가 | Team Topologies 모델 기반 정렬 | |
지속 가능성 | 기술 부채 관리 | 급격한 확장은 설계 복잡도 및 유지 비용 증가 | 기술 부채 대시보드 관리, 의존성 최소화, 핵심만 우선 구현 |
학습 곡선 완화 | DDD 는 설계와 개념이 복잡해 초기 진입 장벽 존재 | 컨텍스트 단위 도입, DDD-lite, 파일럿 적용부터 확장 | |
기타 주의점 | 과도한 도메인 분할 | 너무 많은 Subdomain 은 경계 관리와 개발 리소스 부담 | 핵심 도메인 위주로 우선 모델링, 비핵심은 최소화 |
이벤트 남용 | 무분별한 이벤트 생성은 처리 복잡도 증가 | 이벤트 필터 적용, 도메인 의미 중심 이벤트 설계 | |
설계 복잡도 관리 | Context 및 레이어 수가 많아지면 추적 어려움 | 관계 시각화 도구 (Context Mapper, C4 Model 등) 활용 |
도메인 주도 설계와 고급 아키텍처 연계
CQRS (Command Query Responsibility Segregation) 와 DDD
개념 설명:
CQRS 는 " 명령 (Command)” 과 " 조회 (Query)” 를 책임 단위로 분리하는 아키텍처 패턴이다.
DDD 에서 복잡한 도메인의 상태 변경과 조회를 분리함으로써, 도메인 로직은 더 명확해지고 성능 최적화도 가능하다.
구조 다이어그램 (Mermaid):
graph TD UI --> CommandHandler UI --> QueryHandler CommandHandler --> DomainModel DomainModel --> EventStore QueryHandler --> ReadModelDB
요소 | 설명 |
---|---|
CommandHandler | 도메인 상태를 변경하는 명령 처리 책임 |
QueryHandler | 별도 DB 또는 Projection 을 통해 조회 처리 |
ReadModelDB | 조회 전용 데이터 저장소 (예: Redis, View Table 등) |
EventStore | 도메인 변경을 이벤트로 저장 (Event Sourcing 과 함께 사용됨) |
Event Sourcing 과 DDD
개념 설명:
Event Sourcing 은 도메인 객체의 상태를 변경 이벤트 (event) 들의 이력으로 저장하고, 현재 상태는 이 이벤트들을 순차적으로 리플레이하여 생성하는 방식이다.
장점:
- 상태 이력 추적이 가능
- 복구 및 감사 (Audit) 용이
- 비동기 확장성 확보
단점 및 주의점:
- 상태 조회가 복잡해질 수 있음 → Snapshot 도입 고려
- 이벤트 스키마 변경이 까다로움 → 이벤트 버전 관리 체계 도입 필요
Clean Architecture 와 DDD 결합
개념 설명:
Clean Architecture 는 계층 구조를 중심으로 책임을 분리하며, 내부 (core) 와 외부 (infrastructure) 를 명확히 구분한다. DDD 의 도메인 계층은 가장 중심 (Core Domain Layer) 에 위치한다.
계층 구조:
계층 | 설명 |
---|---|
Application Layer | 서비스 오케스트레이션, 유스케이스 구현 |
Domain Layer | 엔티티, 애그리거트, 도메인 서비스 등 |
Infrastructure | DB, 외부 시스템, 리포지토리 구현 등 |
실무 프로젝트 확장 전략 요약
전략 | 목적 | DDD 연계 방식 | 적용 시 고려사항 |
---|---|---|---|
CQRS | 명령과 조회 분리 | 복잡한 읽기/쓰기 로직 분리 | Read Model 최적화, Sync/Async 전략 |
Event Sourcing | 상태 이력 관리 | 도메인 이벤트 저장 | 스냅샷, 이벤트 버전 관리 |
Clean Architecture | 의존성 분리 | 도메인 중심 계층 구조 | 인터페이스 기반 설계 |
API-first 설계 | 명확한 컨텍스트 경계 | API = Bounded Context 인터페이스 | Swagger/OpenAPI 기반 문서화 |
테스트 주도 개발 (TDD) | 도메인 신뢰성 확보 | 도메인 단위 테스트 강화 | 테스트 더블 (Mock, Stub 등) 활용 |
DDD 기반 리팩토링 전략
리팩토링 필요성
- 기존 모놀리식 구조 또는 데이터 중심 설계에서 발생하는 문제점:
- 비즈니스 로직이 서비스 클래스에 분산
- 명확하지 않은 모듈 경계
- 재사용성 및 테스트 어려움
- 도메인 지식이 코드 구조에 반영되지 않음
전체 리팩토링 단계 개요
단계 | 목적 | 주요 활동 |
---|---|---|
1 단계 | 도메인 분석 | 바운디드 컨텍스트 식별, 유비쿼터스 언어 정의 |
2 단계 | 코드 구조 분석 | 기존 레거시 구조를 계층화하고, 책임 분리 |
3 단계 | 전술적 설계 적용 | 엔티티, 값 객체, 애그리거트로 도메인 모델 구현 |
4 단계 | 도메인 계층 리팩토링 | 서비스 → 도메인 객체로 로직 이동 |
5 단계 | 응용 계층 도입 | Application Layer 에서 도메인 호출 제어 |
6 단계 | 통합 최적화 | CQRS, 이벤트 처리 도입 (필요 시) |
리팩토링 전략 상세
1. 도메인 중심 분석
- Event Storming, Use Case Mapping, 업무 플로우 분석 등을 활용해 비즈니스 행위와 개체를 파악한다.
- 바운디드 컨텍스트 (Bounded Context) 식별
도구 추천:
- Miro, Whimsical (Event Storming 협업 도구)
- 도메인 용어 정리 시 유비쿼터스 언어 문서 작성
2. 기존 코드 구조 정리
리팩토링 전 | 리팩토링 후 (계층화) |
---|---|
Controller | Controller (UI Layer) |
Service (비대해짐) | Application Service |
DAO, DTO | Domain Entity, Repository |
- 도메인 로직을 ApplicationService 에서 DomainModel 로 점진적으로 이동
- 단순 CRUD 는 남기되, 복잡한 로직은 도메인 모델화
3. 전술적 설계 도입
도메인 구성요소 | 적용 기준 |
---|---|
엔티티 (Entity) | 고유 ID 가 존재하고 상태 변경이 중요한 객체 |
값 객체 (Value Object) | 동일성보다 속성 자체가 중요한 불변 객체 |
애그리거트 (Aggregate) | 일관성을 유지해야 할 객체 묶음 |
도메인 서비스 | 도메인 객체에 포함되지 않는 로직 |
예시:
4. 도메인 계층 중심 리팩토링
- 서비스 클래스에 있는 규칙 로직을 도메인 엔티티/값 객체로 이동
- 불변성과 명시적인 상태 변경 메서드 사용
- 비즈니스 용어가 코드 내에 명시적으로 드러나도록 구성
5.응용 계층 (Application Layer) 도입
- 컨트롤러와 도메인 사이의 오케스트레이션 역할
- 트랜잭션 관리, 유즈케이스 흐름 제어
- 외부 서비스 호출 분리
6. 통합 및 확장
기술 요소 | 적용 타이밍 | 목적 |
---|---|---|
CQRS 도입 | 읽기/쓰기 요구가 매우 다른 경우 | 성능, 복잡도 분리 |
이벤트 발행 | 바운디드 컨텍스트 간 통신 필요 | 유연한 확장 |
메시지 브로커 | 서비스 간 비동기 통합 | 마이크로서비스 이전 단계로 발전 가능 |
테스트 전략 | 리팩토링 전/후 회귀 방지 | 도메인 단위 테스트, 엔드투엔드 테스트 강화 |
리팩토링 예시 시나리오 (온라인 주문 시스템)
리팩토링 전 | 리팩토링 후 |
---|---|
OrderService 에서 모든 로직 처리 | Order 애그리거트 내부로 도메인 로직 이동 |
주문 상태 변경: service.changeOrderStatus(orderId, status) | order.markAsDelivered() 방식으로 상태 변경 로직 캡슐화 |
단순한 DTO 기반 응답 | 애그리거트에서 Projection 또는 Read Model 제공 |
리팩토링 시 유의사항 및 Best Practice
항목 | 설명 | 권장 전략 |
---|---|---|
점진적 리팩토링 | 대규모 시스템은 전면 재설계보다 점진적 전환이 유효 | 핵심 도메인부터 DDD 적용, 나머지는 Anti-Corruption Layer 로 연결 |
테스트 커버리지 | 리팩토링 안정성 확보에 필수 | 도메인 단위 테스트와 인수 테스트 작성 병행 |
조직 구조 반영 | 팀 조직이 도메인 구조와 일치하지 않으면 컨텍스트 왜곡 가능 | 팀 단위로 바운디드 컨텍스트 유지 (Conway’s Law 고려) |
주제와 관련하여 주목할 내용
카테고리 | 주제 | 핵심 항목 또는 기술 | 설명 |
---|---|---|---|
핵심 개념 | 전략적 설계 | Bounded Context, Context Map | 시스템을 도메인 기반으로 나누고, 각 경계 간 관계를 정의 |
전술적 설계 | Entity, Value Object, Aggregate, Repository 등 | 도메인 로직을 구현하기 위한 객체 중심 설계 방식 | |
유비쿼터스 언어 | 공통 용어 정의 | 개발자와 도메인 전문가 간 커뮤니케이션 통일 | |
패턴 및 설계 기법 | CQRS + Event Sourcing | 읽기/쓰기 분리 및 이벤트 중심 상태 재구성 | 확장성과 상태 추적성을 동시에 확보하는 핵심 설계 방식 |
Saga 패턴 | 분산 시스템에서 보상 트랜잭션으로 일관성 유지 | 원자적 트랜잭션이 어려운 상황에서 데이터 정합성 유지 | |
Specification Pattern | 비즈니스 규칙 캡슐화 및 재사용 | 복잡한 조건 로직을 독립 객체로 관리 | |
Anti-Corruption Layer | 외부 모델 오염 방지 | 외부 시스템과 내부 도메인 모델 간 보호 계층 | |
Domain Storytelling | 사용자 중심 흐름 모델링 | 현업과의 협업 기반 프로세스 시각화 | |
Event-First Design | 이벤트 흐름 중심의 도메인 모델링 | 상태보다 행동 중심으로 도메인을 설계 | |
모델링/협업 도구 | Event Storming | 도메인 이벤트 중심 협업 워크샵 | 스티커 기반 시각화로 도메인 이해 공유 |
Context Mapper / DSL | 바운디드 컨텍스트 간 관계 모델링 도구 | Context Map 을 텍스트 기반으로 정의 가능 | |
Pact / Spring Cloud Contract | 계약 기반 통합 테스트 도구 | API 간 인터페이스 안정성 확보 | |
아키텍처 확장 | Hexagonal Architecture | 포트 - 어댑터 기반 도메인 중심 설계 | 외부와 내부를 명확히 분리하여 유지보수성 향상 |
Onion / Clean Architecture | 계층 구조 기반 의존성 역전 설계 | 핵심 도메인을 외부 계층과 독립적으로 보호 | |
Microservices + DDD | 컨텍스트 기반 서비스 분리 | 도메인 기준 마이크로서비스 설계 | |
Serverless DDD | 이벤트 기반 서버리스와 도메인 중심 모델 통합 | FaaS 구조에서 도메인 독립성 유지 | |
Mesh Architecture | 서비스 메시 기반의 경계 중심 통신 | Bounded Context 간 네트워크 통제 | |
엣지/IoT 아키텍처 | 엣지 환경의 분산 도메인 구성 | 로컬 처리 시스템에 도메인 모델 확장 | |
성능 및 최적화 전략 | CQRS 고도화 | Materialized View, Read Model 최적화 | 읽기 성능 향상 및 확장성 확보 |
이벤트 스트리밍 | Kafka, Streams 등 | 도메인 이벤트의 실시간 처리 및 분석 | |
보안 및 안정성 | Zero Trust Architecture | 최소 권한 기반의 도메인 단위 보안 | 컨텍스트 중심 보안 설계 적용 |
도메인 기반 암호화 | 민감한 도메인 속성에 대한 필드 수준 암호화 | 개인정보 및 기밀 보호 전략 | |
조직/운영 연계 | DevOps + DDD | CI/CD, Observability 통합 | 자동화된 도메인 중심 운영 구조 설계 |
Team Topologies | 팀 구조 ↔ 컨텍스트 정렬 | 팀 조직을 도메인 경계와 일치시킴 | |
Legacy Migration | Strangler 패턴, 점진적 전환 | 레거시 시스템을 도메인 아키텍처로 이동 | |
확장 응용 | Agile 전략 - 전술 순환 | 전술적 피드백 기반 전략 재설계 | 반복적 모델 개선과 커뮤니케이션 강화 |
UX 전략과 전술 | 도메인 기반 UI 리디자인 | 사용자 경험에 도메인 지식 반영 | |
서브도메인 진화 사례 | Generic → Core 진화 | 기능적 중요도 변화에 따른 재분류 가능성 | |
Data Mesh | 도메인 소유 데이터 아키텍처 | 데이터 팀이 도메인 중심으로 독립 운영 가능 |
- 전략적 설계는 " 경계 정의와 컨텍스트 간 통합 " 에 초점.
- 전술적 설계는 " 도메인 로직 구현과 구조화 " 에 중점.
- CQRS + Event Sourcing + Saga는 복잡한 시스템에서 DDD 의 성능과 안정성을 책임지는 핵심 패턴 3 종.
- Event Storming & Storytelling은 도메인 중심 설계 협업에 적합한 모델링 기법.
- Microservices/Serverless/IoT 등 아키텍처 변화에 따라 DDD 가 유연하게 진화하고 있음.
주제와 관련하여 반드시 학습해야 할 내용
카테고리 | 주제 | 핵심 항목 | 설명 |
---|---|---|---|
기초 원칙 | 하위 도메인 식별 (Subdomain ID) | Core / Supporting / Generic | 비즈니스 도메인을 기능 기반으로 분할하여 전략 수립 기반 마련 |
유비쿼터스 언어 (Ubiquitous Language) | 도메인 용어 정립 및 일관성 유지 | 도메인 전문가와 개발자가 공통으로 사용하는 언어 체계 | |
바운디드 컨텍스트 (Bounded Context) | 경계 기반 모델 관리 | 모델, 언어, 로직이 유효한 논리적 경계 단위 설정 | |
도메인 분석 및 비전 설정 | Domain Vision, Core 식별 | 핵심 도메인 중심으로 비즈니스 전략 설계 | |
전략적 설계 (Strategic) | 컨텍스트 매핑 (Context Mapping) | ACL, Shared Kernel, Customer/Supplier 등 | Bounded Context 간 관계 정의 및 통합 방식 설계 |
이벤트 스토밍 (Event Storming) | 도메인 이벤트 탐색 워크숍 | 현업과 개발자 간 협업을 통한 모델 도출 기법 | |
도메인 스토리텔링 (Domain Storytelling) | 액터 기반 시나리오 설명 | 업무 흐름을 인간 중심 내러티브로 표현 | |
Data Mesh 개념 적용 | 도메인별 데이터 소유 | DDD 의 조직화된 데이터 설계 확장 모델 | |
전술적 설계 (Tactical) | Aggregate 설계 및 최적화 | 일관성 경계, 트랜잭션 범위 | 복잡성 제어 및 비즈니스 규칙 캡슐화 |
Entity vs Value Object | 식별자 여부, 불변성 | 도메인 객체 설계 기준 확립 | |
Repository Pattern | 영속성 계층 추상화 | 도메인 로직과 인프라 분리 | |
Factory 패턴 심화 | 복잡한 객체 생성을 도메인 외부로 분리 | 생성 책임 분리 및 일관된 생성 보장 | |
Specification Pattern | isSatisfiedBy(), AND/OR 조합 | 조건 로직 재사용 및 필터링 구조화 | |
Domain Event 설계 | 이벤트 발행/구독, Side Effect 분리 | 상태 변화 기록 및 통합 트리거 | |
도메인 테스트 전략 | 단위/통합 테스트 | 트랜잭션, 규칙 검증 및 코드 품질 확보 | |
Anemic Domain Model 제거 | 비즈니스 로직 분산 방지 | 풍부한 도메인 모델 구현 유도 | |
이벤트 기반 설계 | Event-Driven Integration | 도메인 간 비동기 통합 | 이벤트 기반 메시징 아키텍처 구현 |
Event Sourcing + Snapshotting | 상태 복원 최적화 | 저장 비용 및 성능 균형 확보 | |
Saga 패턴 (보상 트랜잭션) | 트랜잭션 분산 및 실패 처리 | 마이크로서비스 간 데이터 정합성 확보 | |
아키텍처 패턴 | CQRS (명령/조회 분리) | Command ↔ Query 분리 | 쓰기/읽기 분리 통한 성능/유지보수 개선 |
Hexagonal Architecture | Port-Adapter 구조 | 외부 의존성과 도메인 계층 분리 | |
Onion / Clean Architecture | 계층 기반 의존성 역전 구조 | 유연한 테스트 및 유지보수 가능 설계 | |
DIP (Dependency Inversion Principle) | 인터페이스 중심 설계 | 모듈 간 결합도 감소, 구현체 교체 유연성 확보 | |
계약 및 통합 전략 | Anti‑Corruption Layer (ACL) | 번역 계층 통한 외부 모델 보호 | 외부 시스템의 영향 차단 및 내부 모델 보호 |
Shared Kernel / Published Language | 공통 코드/스키마 공유 | 컨텍스트 간 인터페이스 일관성 확보 | |
Open Host Service | 외부 API 공개용 인터페이스 | 안전한 외부 접근 통제 및 통합 채널 제공 | |
Conformist / Customer-Supplier | 통합 유형에 따른 상하류 관계 정의 | 컨텍스트 간 의존 구조 유형 구분 | |
조직 및 실무 역량 | 마이크로서비스 설계 기반 DDD 적용 | 컨텍스트 기반 서비스 분리 | 독립적 배포 가능 구조 설계 |
DDD + DevOps/SRE 연계 전략 | 테스트, 모니터링, 배포 통합 | 운영 자동화 기반의 도메인 설계 정착 | |
스트랭글러 패턴 / 기술 부채 해소 | 레거시 → 신규 시스템 점진 전환 | 이관 위험 최소화와 진화적 리팩토링 지원 |
용어 정리
카테고리 | 용어 | 설명 |
---|---|---|
1. 핵심 개념 | Domain (도메인) | 소프트웨어가 해결하고자 하는 실제 비즈니스 문제 영역 |
Domain Model (도메인 모델) | 도메인의 개념과 규칙을 코드로 표현한 추상 모델 | |
Subdomain (하위 도메인) | 도메인을 기능적으로 분할한 의미 있는 하위 영역 | |
Core Domain (핵심 도메인) | 시스템의 비즈니스 차별성을 구성하는 중심 영역 | |
Supporting Subdomain (지원 도메인) | 핵심 도메인을 지원하는 기능적 보조 영역 | |
Generic Subdomain (일반 도메인) | 여러 시스템에서 공통으로 사용되는 범용 기능 영역 | |
Ubiquitous Language (유비쿼터스 언어) | 도메인 전문가와 개발자가 공유하는 공통 용어 체계 | |
2. 전략 설계 (Strategic Design) | Bounded Context (경계 컨텍스트) | 하나의 모델과 언어가 일관되게 유지되는 설계 단위 경계 |
Context Map (컨텍스트 맵) | 여러 Bounded Context 간의 관계를 시각화한 설계도 | |
Event Storming | 도메인 이벤트를 중심으로 도메인 프로세스를 시각화하는 협업 기법 | |
Domain Storytelling | 실제 행위자와 프로세스를 기반으로 비즈니스 흐름을 설명하는 기법 | |
Data Mesh | 도메인 중심의 분산된 데이터 소유 및 제공 구조 | |
3. 전술 설계 (Tactical Design) | Entity (엔티티) | 고유 식별자 (ID) 를 갖고 생명주기와 상태를 가지는 객체 |
Value Object (값 객체) | 불변이며, 속성 값으로 동일성을 판단하는 객체 | |
Aggregate (애그리게이트) | 일관성을 보장하는 Entity 와 Value Object 의 묶음 | |
Aggregate Root (애그리게이트 루트) | 애그리게이트 내에서 외부 접근을 책임지는 중심 엔티티 | |
Domain Service (도메인 서비스) | 특정 엔티티에 귀속되지 않는 도메인 로직을 캡슐화한 서비스 | |
Repository (리포지토리) | 도메인 객체의 조회·저장·삭제 등 영속성 처리를 담당하는 인터페이스 | |
Factory (팩토리) | 복잡한 도메인 객체의 생성 과정을 담당하는 생성 패턴 | |
Domain Event (도메인 이벤트) | 도메인 상태 변화나 비즈니스 사건을 표현하는 객체 | |
Specification (명세 패턴) | 도메인 객체의 조건 검증 및 필터링 로직을 객체로 추상화한 설계 기법 | |
4. 아키텍처 및 설계 패턴 | Layered Architecture (레이어드 아키텍처) | 프레젠테이션, 애플리케이션, 도메인, 인프라 계층 구조 |
Hexagonal Architecture (헥사고날 아키텍처) | 포트와 어댑터를 통한 내부 도메인과 외부 시스템의 분리 설계 | |
CQRS (명령/조회 책임 분리) | Command 와 Query 를 분리하여 확장성과 성능 최적화 | |
Event Sourcing | 도메인 상태를 이벤트의 흐름으로 저장하고 복원하는 방식 | |
DIP (의존성 역전 원칙) | 추상화에 의존하도록 하여 상위 모듈이 하위 모듈에 의존하지 않게 설계 | |
5. 통합 및 경계 보호 (Integration) | Anti-Corruption Layer (ACL) | 외부 시스템과 내부 도메인 모델 간의 변환 계층 |
Shared Kernel | 여러 Context 간에 공유되는 안정적인 도메인 모델 조각 | |
Open Host Service | 외부 시스템이 접근할 수 있도록 공개된 인터페이스 (API) 제공 계층 | |
Published Language | 시스템 간 통합을 위해 명시적으로 정의된 공통 메시지 형식 | |
Customer/Supplier | 상류/하류 컨텍스트 간 요청 - 응답 기반 통합 관계 | |
Conformist | 하류 컨텍스트가 상류의 모델을 그대로 따르는 통합 패턴 | |
6. 테스트 및 구현 | BDD (Behavior Driven Development) | 유비쿼터스 언어 기반의 테스트 자동화 및 시나리오 중심 개발 기법 |
참고 및 출처
개요 및 핵심 개념
- Domain‑Driven Design – Wikipedia
- Domain‑Driven Design 공식 사이트 – Eric Evans
- DDD 문서화 – dddcommunity.org
- Bounded Context – Martin Fowler
- CQRS Pattern – Martin Fowler
- Event Sourcing – Microsoft Docs
- Domain‑Driven Design – GeeksforGeeks
도서 및 저자 원문
- Domain-Driven Design: Tackling Complexity in the Heart of Software – Eric Evans
- Implementing Domain-Driven Design – Vaughn Vernon
- Vaughn Vernon – DDD Distilled
Strategic Design 관련 자료
- Domain-Driven Design (DDD): Strategic Design Explained – Medium
- Strategic Domain Driven Design with Context Mapping – InfoQ
- Strategic Design – DevIQ
- Strategic Design vs. Tactical Design – UX Collective
- DDD and Context Mapper – Mimacom
- Subdomain Evolution in Strategic and Tactical Design – Scaibu
- Strategic Design in DDD – DevIQ
Tactical Design 관련 자료
- Domain-Driven Design—Tactical Design | by Masoud Chelongar
- Using Tactical DDD to Design Microservices – Microsoft
- Tactical Design in DDD – DevIQ
- DDD Part 2: Tactical Domain-Driven Design – Vaadin
- Tactical Design in Domain-Driven Design – LinkedIn Pulse
통합 및 사례 중심 자료
- Domain Driven Design in 10 minutes – Thoughtworks
- Domain-driven design in functional programming – Thoughtworks
- Using Domain Analysis to Model Microservices – Microsoft
- CQRS & Event Sourcing – POPIT
- Event Storming – IBM Cloud Architecture
- DDD for Microservices: Complete Guide – Sayonetech
- DDD Example: E-commerce Microservices – GitHub (ttulka)
- Clean Architecture – Uncle Bob (Robert C. Martin)
전략 - 전술 균형에 관한 추가 해설
- How do you balance between strategic and tactical design in DDD? – LinkedIn
- Strategic vs Tactical Planning in Enterprise Architecture – ValueBlue
- Strategic Design vs Tactical Design – Helsinki Design Lab