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) 의 한계를 극복하기 위해 등장했다.

전통적인 소프트웨어 개발에서는 다음과 같은 문제점들이 있었다:

목적 및 필요성

주요 목적

  1. 비즈니스 - 기술 격차 해소: 비즈니스와 소프트웨어 개발 간의 격차를 줄이고 기술 팀과 도메인 전문가 간의 명확한 소통 촉진
  2. 복잡성 관리: 복잡한 비즈니스 로직을 캡슐화하는 도메인 모델을 통해 비즈니스 현실과 코드 간의 격차 해소
  3. 시스템 모듈화: 대규모 도메인을 여러 Bounded Context 로 분할하여 명시적 관계 설정

필요성

주요 기능 및 역할

전략적 설계 (Strategic Design)

전술적 설계 (Tactical Design)

특징

핵심 특징

  1. 도메인 중심성: 비즈니스 도메인이 설계의 중심
  2. 협업 지향: 도메인 전문가와 개발자의 긴밀한 협력
  3. 진화적 설계: 지속적인 학습과 개선을 통한 모델 발전
  4. 명시적 모델링: 암묵적 지식을 명시적 코드로 표현

구별되는 특징

핵심 원칙

DDD 의 핵심 원칙은 도메인 중심의 설계와 유비쿼터스 언어의 사용이다. 도메인 전문가와 개발자가 협업하여 도메인 모델을 구축하고, 이를 소프트웨어에 반영한다. 또한, 바운디드 컨텍스트를 정의하여 도메인 모델의 적용 범위를 명확히 한다.

DDD 의 3 가지 핵심 원칙

  1. Focus on the Core Domain (핵심 도메인에 집중)

    • 비즈니스 경쟁 우위를 제공하는 핵심 영역에 집중
    • 제한된 자원을 가장 중요한 부분에 투자
  2. Explore Models in Collaboration (협력을 통한 모델 탐구)

    • 도메인 전문가와 개발자의 지속적 협력
    • 공동 모델링을 통한 깊은 도메인 이해
  3. Speak Ubiquitous Language (유비쿼터스 언어 사용)

    • 모든 이해관계자가 동일한 언어 사용
    • 코드와 대화에서 일관된 용어 유지
graph TD
    A[도메인 전문가] --> B[유비쿼터스 언어]
    C[개발자] --> B
    D[테스터] --> B
    E[비즈니스 분석가] --> B
    
    B --> F[코드]
    B --> G[문서]
    B --> H[대화]
    B --> I[테스트]

설계 원칙

  1. Model-Driven Design: 모델과 구현의 긴밀한 연결
  2. Hands-On Modelers: 모델러가 직접 코드 작성 참여
  3. 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: 지속적인 협력과 개선
  1. 도메인 지식 공유 (DE ➡ DEV)
    도메인 전문가 (Subject Matter Expert) 는 개발자에게 비즈니스의 규칙, 용어, 흐름 등 도메인 지식을 전달한다.
    이는 유비쿼터스 언어 (Ubiquitous Language) 의 형성 및 모델링의 시작점이다.

  2. 초기 모델링 (DEV ➡ MODEL)
    개발자는 공유받은 도메인 지식을 바탕으로 개념 모델을 만듭니다. 이 모델은 엔티티, 값 객체, 애그리게이트 등을 포함한다.

  3. 코드 구현 (MODEL ➡ CODE)
    모델링된 구조를 바탕으로 실제 코드로 구현한다. 이 때, 애플리케이션 레이어, 도메인 레이어, 인프라스트럭처 레이어 등 계층적 아키텍처 설계를 따르게 된다.

  4. 구현을 통한 학습 (CODE ➡ DEV)
    코드를 작성하고 테스트하는 과정에서 개발자는 도메인에 대한 새로운 이해를 얻게 되고, 때로는 기존 이해에 오류가 있었음을 발견한다.

  5. 질문과 확인 (DEV ➡ DE)
    개발자는 도메인 전문가에게 다시 질문하거나 모델에 대한 확인을 요청한다. 이 과정은 도메인에 대한 해석 오류 방지를 위한 중요한 피드백 루프이다.

  6. 피드백 제공 (DE ➡ DEV)
    도메인 전문가는 잘못된 부분을 바로잡거나, 새로운 도메인 규칙 또는 예외 상황을 공유한다.

  7. 모델 개선 (DEV ➡ MODEL)
    피드백을 반영해 도메인 모델을 재구성하거나 수정한다. 이는 종종 이벤트 스토밍, 컨텍스트 매핑 등 협업 기법을 통해 이루어진다.

  8. 리팩토링 (MODEL ➡ CODE)
    개선된 도메인 모델에 따라 코드를 리팩토링한다. 이 과정은 기술 부채를 줄이고, 도메인 변경에 유연한 구조를 만드는데 핵심이다.

전략적 설계 (Strategic Design)

도메인 전체를 비즈니스 관점에서 논리적으로 분해하고, 시스템을 경계 (Context) 단위로 나누는 고수준 설계 전략.

주요 목적

핵심 구성요소

구성요소설명
Subdomain시스템 내 비즈니스 하위 영역 (Core, Supporting, Generic) 으로 분류됨
Bounded Context특정 모델이 일관되게 사용되는 경계. 설계·언어·의미가 명확히 정리됨
Context Map여러 Bounded Context 간의 관계를 정의하는 지도. 공유 커널, ACL, 고객 - 공급자 등 포함

예시:

Subdomain 의 세 가지 분류

구분정의특징예시
Core Subdomain시스템이 경쟁력을 갖기 위해 반드시 잘 설계되어야 하는 핵심 비즈니스 영역- 조직만의 차별화 요소
- 가장 많은 투자가 이루어져야 함
- 고급 도메인 전문가와의 협업 필수
주문 처리 로직 (전자상거래), 대출 승인 알고리즘 (금융), 물류 최적화 (배송)
Supporting SubdomainCore 를 지원하지만, 핵심 가치는 아니며 핵심 기능의 성능이나 유효성을 보완- 외부 구매 가능성 낮음
- 복잡할 수 있으나 전략적 가치 낮음
- 위임하거나 내재화 가능
사용자 관리, 권한 시스템, 알림 처리, 보고서 생성
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)도메인 상태 변화 발생 시 발행되는 이벤트 (예: 주문 생성됨, 결제 완료됨 등)

→ 각 요소는 다음과 같이 연결된다:

Bounded Context B 의 구성:

→ 이는 하나의 다른 서브 도메인 또는 마이크로서비스가 독립적으로 설계된 구조를 보여준다.

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도메인에서 발생한 중요한 사건 모델링
RepositoryAggregate 의 영속성 처리 인터페이스
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 계층 구조를 따른다:

  1. 프레젠테이션 계층 (Presentation Layer)
  2. 응용 계층 (Application Layer)
  3. 도메인 계층 (Domain Layer)
  4. 인프라스트럭처 계층 (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 ServiceEntity 나 VO 에 속하지 않는 순수 도메인 로직을 처리하는 무상태 객체.- Stateless
- 비즈니스 로직 응집
- 여러 Aggregate 사용 가능
- 외부 로직 캡슐화
- 복합 연산 수행
- 규칙/조건 검증
TransferService, TaxCalculator
Domain Event도메인 상태 변화나 의미 있는 사건을 표현하는 불변 객체.- 발생 시점 기록
- 비동기 처리 용이
- 불변 객체
- 로깅, 통지, 트리거
- 이벤트 기반 처리 유도
UserRegisteredEvent, OrderShippedEvent
Repository InterfaceAggregate 단위로 저장/조회하는 추상화된 영속성 인터페이스.- 기술 독립성 유지
- 의존 역전
- 도메인 계층 분리 가능
- Aggregate 저장/조회
- 도메인 계층 내 기술 분리
OrderRepository, UserRepository
Specification (선택)비즈니스 규칙을 명시적 객체로 분리하여 재사용/조합 가능하게 표현.- 재사용성
- 명세 캡슐화
- 조합 가능 (AND/OR 등)
- 조건 로직 추상화
- 복잡한 필터/조건 캡슐화
AvailableProductSpec, CustomerIsVIPSpec
Factory (선택)복잡한 객체 생성 로직을 캡슐화하고 생성을 단순화하기 위한 책임 분리 객체.- 생성 책임 단일화
- 복잡한 생성 절차 추상화
- 재사용성 높음
- 복합 Aggregate/Entity 생성
- 객체 생성을 도메인 계층에 포함시키지 않음
OrderFactory, AccountFactory

전략적 설계 (Strategic Design) vs. 전술적 설계 (Tactical Design)

Strategic Design무엇을 왜 구축할지에 대한 비즈니스 도메인의 전체적인 구조와 경계를 정의하는 고수준 설계 접근법이며, Tactical Design어떻게 구현할지에 대한 도메인 모델의 구체적인 구현을 위한 저수준 패턴과 기법을 제공한다.

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 DesignTactical Design
비즈니스 정렬비즈니스 요구사항과 높은 정렬성명확한 비즈니스 로직 표현
팀 협업팀 간 명확한 경계 설정, 자율성 확보개발자 간 코드 구조 일관성 유지
유지보수성장기적으로 안정적인 아키텍처 설계 가능코드 품질 향상, 테스트 용이성, 재사용 가능한 도메인 모델 구성
확장성도메인 중심 시스템 확장 시 경계 기반 설계 가능모듈화된 구조로 유연한 기능 추가 및 변경 용이
시스템 구조전체 시스템의 큰 그림 설계에 유리실제 코드와 동작 구현에 직접적으로 연결되는 모델링 수행 가능
약점
관점Strategic DesignTactical 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 MapBounded 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 중심 구조:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Aggregate Root (중심)
├── Entity (포함)
├── Value Object (포함)
└── Domain Event (발행)

외부 지원:
├── Repository (저장/조회)
├── Factory (생성)
├── Domain Service (조작)
└── Specification (규칙 검증)

계층별 책임 분할:

계층포함 요소주요 책임
도메인 모델 계층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 ContextModule 구조하나의 Context = 하나의 Module 그룹
Ubiquitous Language클래스/메서드 명명도메인 용어가 직접 코드에 반영
Context MapIntegration EventContext 간 통신을 위한 Domain Event 설계

도메인 모델의 각 Bounded Context 에 대해 도메인을 모델링하는 엔티티, 값 객체 및 집합체를 식별하고 정의한다. 컨텍스트를 정의하는 경계 내에 포함된 도메인 모델을 구축하고 개선한다.

단계별 변환 과정

변환 단계:

  1. 도메인 탐색Bounded Context 식별Aggregate 후보 발견
  2. Ubiquitous LanguageEntity/Value Object 명명메서드 시그니처 정의
  3. Context MapIntegration PatternDomain Event 설계
  4. Subdomain 분류Module 구조Package/Namespace 조직화
1 단계: 도메인 탐색 → Bounded Context 식별 → Aggregate 후보 발견

무엇을 만들 것인가? 에서 어떻게 나눌 것인가? 로의 변환

핵심 활동:

입력물 (Input)

항목설명예시
비즈니스 요구사항도메인 전문가가 정의한 핵심 기능" 고객이 상품을 주문하고 결제한다 "
도메인 지식업무 프로세스와 비즈니스 규칙" 재고가 없으면 주문할 수 없다 "
이해관계자 인터뷰각 영역별 전문가 의견주문팀, 배송팀, 결제팀 인터뷰

출력물 (Output)

항목설명예시
Bounded Context Map컨텍스트 간 관계도Order ↔ Payment ↔ Shipping
Aggregate 후보 목록일관성 경계별 객체 그룹Order(OrderItem, Address), Product(Category, Price)
Context 책임 정의각 컨텍스트의 역할과 범위Order Context: 주문 생성/수정/취소 담당

핵심 의사결정:

2 단계: 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)

핵심 의사결정:

3 단계: 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()

핵심 의사결정:

4 단계: Subdomain 분류 → Module 구조 → Package/Namespace 조직화

도메인 분류물리적 코드 구조로 변환하는 최종 단계

핵심 활동:

입력물 (Input):

항목설명예시
Subdomain 분류비즈니스 우선순위 분류Core: Order, Supporting: Customer, Generic: Payment
아키텍처 제약사항기술적 제약과 표준Java 패키지 명명 규칙
팀 구조개발팀 조직과 책임Order 팀, Payment 팀

출력물 (Output):

항목설명예시
Package 구조코드 조직화 방식com.company.order.domain
Module 의존성모듈 간 관계 정의Order → Customer (readonly)
배포 단위물리적 배포 구조JAR, Docker Container

조직화 원칙:

  1. 도메인 우선: 기술 레이어보다 도메인 개념으로 그룹화
  2. 응집성: 함께 변경되는 코드를 함께 배치
  3. 낮은 결합도: 모듈 간 의존성 최소화
단계별 변환 과정 예시: 전자상거래 도메인 분석

변환 단계:

  1. 도메인 탐색Bounded Context 식별Aggregate 후보 발견
  2. Ubiquitous LanguageEntity/Value Object 명명메서드 시그니처 정의
  3. Context MapIntegration PatternDomain Event 설계
  4. Subdomain 분류Module 구조Package/Namespace 조직화
1 단계: 도메인 탐색 → Bounded Context 식별 → Aggregate 후보 발견

핵심 활동:

도메인 탐색 결과:
- 핵심 비즈니스 프로세스: 상품 진열 → 장바구니 → 주문 → 결제 → 배송
- 도메인 전문가 식별: 상품 관리자, 주문 관리자, 배송 담당자, 고객 서비스

Bounded Context 식별:

1
2
3
4
5
6
7
E-commerce Domain
├── Product Catalog Context (상품 카탈로그)
├── Shopping Cart Context (장바구니)  
├── Order Management Context (주문 관리)
├── Payment Context (결제)
├── Shipping Context (배송)
└── Customer Context (고객 관리)

Aggregate 후보 발견:

Bounded ContextAggregate Root포함 Entity/Value Object
Product CatalogProductCategory, Price, ProductImage
Shopping CartCartCartItem, Money
Order ManagementOrderOrderItem, ShippingAddress, BillingAddress
PaymentPaymentPaymentMethod, Money, TransactionResult
ShippingShipmentTrackingNumber, DeliveryAddress
2 단계: Ubiquitous Language → Entity/Value Object 명명 → 메서드 시그니처 정의

핵심 활동:

언어 구체화 실무 예시:

1
2
3
4
5
6
7
8
9
비즈니스 용어          → Entity/Value Object     → 메서드 시그니처
─────────────────────────────────────────────────────────────
"상품을 장바구니에 담기"  → Cart.addProduct()      → void addProduct(ProductId, Quantity)
"주문 취소"           → Order.cancel()         → OrderResult cancel(CancellationReason)
"배송 시작"           → Shipment.start()       → void start(DeliveryAddress, EstimatedDate)
"결제 승인"           → Payment.approve()      → PaymentResult approve(Amount, PaymentMethod)
"재고 확인"           → Product.checkStock()   → StockLevel checkStock(Quantity)
"할인 적용"           → Cart.applyDiscount()   → Money applyDiscount(DiscountCode)
"주문 상태 변경"       → Order.updateStatus()  → void updateStatus(OrderStatus, Timestamp)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 비즈니스 용어: "금액과 통화"
class Money:
	def __init__(self, amount: Decimal, currency: str):
		if amount < 0:
			raise ValueError("금액은 음수일 수 없습니다")
		self._amount = amount
		self._currency = currency
	
	def add(self, other: 'Money') -> 'Money':
		if self._currency != other._currency:
			raise ValueError("다른 통화는 더할 수 없습니다")
		return Money(self._amount + other._amount, self._currency)

# 비즈니스 용어: "배송 주소"
class DeliveryAddress:
	def __init__(self, street: str, city: str, postal_code: str, country: str):
		self._street = street
		self._city = city
		self._postal_code = postal_code
		self._country = country
	
	def get_delivery_zone(self) -> DeliveryZone:
		# 배송 구역 결정 로직
		pass
3 단계: Context Map → Integration Pattern → Domain Event 설계

핵심 활동:

컨텍스트 통합 패턴 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
Order Context (Upstream) → Payment Context (Downstream)
관계: Customer/Supplier
패턴: Open Host Service
구현: PaymentAPI

Shopping Cart Context ↔ Product Catalog Context  
관계: Partnership
패턴: Shared Kernel
구현: ProductSharedModel

Order Context → Shipping Context
관계: Publisher/Subscriber  
패턴: Domain Events
구현: OrderPlacedEvent, ShipmentRequestedEvent
 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
# Order Context에서 발생하는 이벤트
@dataclass
class OrderPlacedEvent:
	order_id: OrderId
	customer_id: CustomerId
	total_amount: Money
	shipping_address: DeliveryAddress
	occurred_at: datetime
	items: List[OrderItem]

# Payment Context에서 소비하는 이벤트  
@dataclass
class PaymentRequiredEvent:
	order_id: OrderId
	amount: Money
	payment_method: PaymentMethod
	due_date: datetime

# Shipping Context에서 발생하는 이벤트
@dataclass  
class ShipmentStartedEvent:
	shipment_id: ShipmentId
	order_id: OrderId
	tracking_number: TrackingNumber
	estimated_delivery: datetime
4 단계: Subdomain 분류 → Module 구조 → Package/Namespace 조직화

핵심 활동:

모듈 구조 실무 예시:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
Core Domain → Primary Modules
├── com.ecommerce.catalog.domain
├── com.ecommerce.order.domain  
└── com.ecommerce.cart.domain

Supporting Domain → Secondary Modules
├── com.ecommerce.customer.domain
├── com.ecommerce.inventory.domain
└── com.ecommerce.notification.domain

Generic Domain → Infrastructure Modules  
├── com.ecommerce.payment.infrastructure
├── com.ecommerce.shipping.infrastructure
└── com.ecommerce.authentication.infrastructure
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
com.ecommerce.order.domain/
├── model/
│   ├── Order.java              # Aggregate Root
│   ├── OrderItem.java          # Entity  
│   ├── OrderStatus.java        # Value Object
│   ├── ShippingAddress.java    # Value Object
│   └── Money.java              # Shared Value Object
├── service/
│   ├── OrderService.java       # Domain Service
│   └── PricingService.java     # Domain Service
├── repository/
│   └── OrderRepository.java    # Repository Interface
├── event/
│   ├── OrderPlacedEvent.java   # Domain Event
│   └── OrderCancelledEvent.java
└── factory/
	└── OrderFactory.java       # Factory
고급 변환 패턴
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 결정사항:

Tactical Design 구현:

 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
# Product Catalog Context의 Product
class CatalogProduct:
    def __init__(self, product_id: ProductId, name: str, description: str):
        self._id = product_id
        self._name = name
        self._description = description
        self._categories: List[Category] = []
        self._images: List[ProductImage] = []
    
    def add_to_category(self, category: Category):
        """상품을 카테고리에 추가"""
        self._categories.append(category)
    
    def update_description(self, new_description: str):
        """상품 설명 업데이트"""
        self._description = new_description

# Order Context의 Product (다른 관점)
class OrderProduct:
    def __init__(self, product_id: ProductId, price: Money, weight: Weight):
        self._id = product_id
        self._price = price
        self._weight = weight
    
    def calculate_shipping_cost(self, destination: DeliveryAddress) -> Money:
        """배송비 계산 (주문 컨텍스트의 관심사)"""
        return self._weight.calculate_shipping_cost(destination)
변환 시 주의사항 및 모범 사례

체계적인 변환 과정을 통해 Strategic Design 의 비즈니스 인사이트가 Tactical Design 의 구체적인 코드 구조로 정확하게 번역되며, 유지보수 가능하고 진화 가능한 시스템을 구축할 수 있다.

언어 일관성 유지
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 좋은 예: 비즈니스 용어 직접 사용
class Order:
    def place_order(self): pass
    def cancel_order(self): pass
    def ship_order(self): pass

# 나쁜 예: 기술적 용어 사용  
class Order:
    def create(self): pass
    def delete(self): pass  
    def update_status(self): pass
Context 경계 명확화
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Order Context
class OrderCustomer:
    def __init__(self, customer_id: CustomerId, shipping_address: Address):
        self._id = customer_id
        self._shipping_address = shipping_address

# Customer Context  
class Customer:
    def __init__(self, customer_id: CustomerId, profile: CustomerProfile):
        self._id = customer_id
        self._profile = profile
        self._loyalty_points = LoyaltyPoints()
이벤트 기반 통합
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Context 간 통신을 위한 이벤트
class OrderPlacedEvent:
    def __init__(self, order_id: OrderId, items: List[OrderItem]):
        self.order_id = order_id
        self.items = items
        self.occurred_at = datetime.now()

# 다른 Context에서 이벤트 처리
class InventoryService:
    def handle_order_placed(self, event: OrderPlacedEvent):
        for item in event.items:
            self.reserve_stock(item.product_id, item.quantity)

상향식 피드백 (Tactical → Strategic)

도메인 모델과 소프트웨어는 도메인 전문가와 사용자의 피드백을 바탕으로 지속적으로 개선된다. 이를 통해 비즈니스 도메인이 진화함에 따라 소프트웨어가 정확하고 관련성을 유지할 수 있다.

피드백 유형:

피드백 신호발생 위치Strategic 영향조치 방향
Aggregate 비대화Entity/Value ObjectContext 경계 재검토컨텍스트 분할
복잡한 Domain Service여러 Aggregate 간Subdomain 재정의새로운 Core Domain 추출
Performance 이슈Repository/QueryContext Map 수정통합 패턴 변경
언어 불일치코드 vs 비즈니스Ubiquitous Language 정제용어 표준화

실무 피드백 패턴:

Tactical 발견Strategic 개선실무 예시
Aggregate 복잡성Context 경계 재검토너무 큰 Aggregate 는 Context 분할 신호
Domain Event 패턴Context 관계 개선이벤트 흐름을 통한 의존성 파악
Repository 복잡성Subdomain 재분류데이터 접근 패턴을 통한 도메인 이해
언어 불일치Ubiquitous Language 정제구현 과정에서 발견되는 용어 모호성

피드백의 트리거 조건:

지속적 개선 사이클

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):

피드백 흐름 (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) 패턴을 보인다. 서로 다른 출발점에서 시작하여 점차 일치된 도메인 모델로 수렴해간다.

수렴 지표:

구현 기법

Strategic Design 구현 기법

기법명정의 및 목적구성 요소실제 예시
Event Storming도메인 이벤트 중심으로 프로세스를 시각화하여 도메인 흐름 이해도메인 전문가, 개발자, 스티커, 타임라인, 이벤트주문 생성 → 결제 완료 → 배송 시작
Context MappingBounded Context 간 관계 및 통합 전략 설계Upstream/Downstream, ACL, Conformist 등 통합 패턴주문 ↔ 결제: Customer/Supplier 관계
Domain Storytelling도메인 흐름을 사람 중심의 서사로 표현하여 업무 이해 공유액터, 객체, 순차적 행동 흐름창고 직원이 주문을 수령하고 재고를 조정
Subdomain 분류도메인을 Core, Supporting, Generic 으로 구분해 중요도 및 리소스 결정비즈니스 가치 기반 도메인 식별 및 분류결제 = Core, 고객 지원 = Supporting
Context Mapper DSLBounded Context 정의를 코드로 표현하여 명확한 의사소통 및 설계 가능DSL 기반 정의 파일, Context, Relationcontextmap.dsl 파일을 통한 설계 시각화

Tactical Design 구현 기법

기법명정의 및 목적구성 요소실제 예시
Aggregate Design일관성 유지 단위를 정의하고 복잡한 도메인 객체를 하나로 묶음Aggregate Root, Entity, Value ObjectOrder + OrderItems + Money 객체
Entity/ValueObject 구현식별 가능/불가능 객체로 모델링하여 도메인 표현력 향상불변성, 동등성 비교, 생성자 유효성 검증Customer 엔티티, Email 값 객체
Repository Pattern도메인 모델의 저장소 추상화를 통해 인프라와 도메인 코드 분리인터페이스, 구현체, DB 연동 코드CustomerRepository / JpaCustomerRepository
Domain ServiceEntity 에 포함시키기 어려운 도메인 로직을 외부에서 처리Stateless 함수, 복합 도메인 계산할인 정책 적용 서비스
Factory Pattern복잡한 Aggregate 생성을 캡슐화하여 생성 책임 분리정적 팩토리 메서드, 빌더 패턴OrderFactory.createOrder()
Specification Pattern도메인 조건 및 검증을 객체로 분리해 재사용 및 조합 가능isSatisfiedBy(), AND/OR/NOT 조합특정 조건의 고객만 조회하는 쿼리 명세
Hexagonal Architecture도메인 중심 아키텍처로 외부 인터페이스와 내부 모델을 분리Adapter (Inbound/Outbound), Application, DomainREST 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 ConsistencyBounded 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, PactContext 간 통합 보장, 회귀 오류 최소화
분산 트랜잭션 처리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:

  1. 주문 생성 → 주문 이벤트 발생
  2. 결제 컨텍스트에서 주문 이벤트 수신 후 결제 처리
  3. 결제 성공 시 결제 이벤트 발생
  4. 배송 컨텍스트에서 결제 이벤트 수신 후 배송 처리

역할:

사례 2: 카카오페이 여신코어 시스템 구축

시스템 구성

시스템 구성도:

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 역할 및 효과:

  1. 유비쿼터스 언어 정립: 금융 도메인의 복잡한 용어를 개발자와 금융 전문가가 공통으로 사용
  2. 바운디드 컨텍스트 분리: 대출, 심사, 납부 등 각 영역의 독립적 발전 가능
  3. 애그리거트 설계: 복잡한 여신 규칙을 대출 애그리거트 내에서 일관성 있게 관리
  4. 도메인 이벤트 활용: 비동기 처리를 통한 시스템 성능 향상

기존 시스템과의 차이점:

사례 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 역할 담당:

  1. Strategic Design:

    • Bounded Context 식별 (고객관리, 주문관리, 배달관리)
    • Context Map 을 통한 통합 지점 정의
  2. Tactical Design:

    • Order Aggregate: 주문 일관성 보장
    • Domain Events: OrderPlaced, PaymentCompleted, DeliveryStarted
    • Repository: 각 Aggregate 에 대한 영속성 관리
  3. Ubiquitous Language: " 주문 “, " 배달 “, " 음식점 " 등 비즈니스 용어 코드에 반영

사례 4: 헬스케어 조직–환자 관리 시스템

목적: 환자 관리 (Patient Management) 를 별도의 Bounded Context 로 분리하여 개발 독립성 확보

시스템 구성:

graph LR
  PatientB(ctx:환자관리) --> BillingB(ctx:청구)
  PatientB --> AppointmentB(ctx:예약)

구성요소:

Workflow:

  1. 워크숍 통해 환자 - 주소 - 진료 이벤트 도출 (EventStorming)
  2. 환자 컨텍스트에서 Entity/VO 구현
  3. 청구/예약 컨텍스트와 OCR/ACL 방식으로 통합
  4. 독립 서비스로 배포 및 이벤트 기반 연동 설계

사례 5: 전자상거래 시스템 사례

시스템 구성:

시스템 구성 다이어그램

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 적용Tactical Design 적용
팀 구조컨텍스트별 독립적인 팀 운영단일 팀 내 역할 분담
데이터베이스컨텍스트별 독립적인 데이터 저장소애그리게이트별 테이블 설계
API 설계컨텍스트 간 명확한 계약 정의도메인 객체 기반 인터페이스
변경 영향도컨텍스트 경계 내로 제한애그리게이트 경계 내로 제한

구현 예시

온라인 쇼핑몰 주문 처리 시스템 (Python)

  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
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
# Domain-Driven Design 기반 온라인 쇼핑몰 주문 시스템

from abc import ABC, abstractmethod
from dataclasses import dataclass
from datetime import datetime
from decimal import Decimal
from enum import Enum
from typing import List, Optional
from uuid import UUID, uuid4

# ============================================================================
# 1. 도메인 계층 (Domain Layer)
# ============================================================================

# 밸류 오브젝트 (Value Objects)
@dataclass(frozen=True)  # 불변성 보장
class Money:
    """금액을 표현하는 밸류 오브젝트"""
    amount: Decimal
    currency: str = "KRW"
    
    def add(self, other: 'Money') -> 'Money':
        if self.currency != other.currency:
            raise ValueError("다른 통화는 더할 수 없습니다")
        return Money(self.amount + other.amount, self.currency)
    
    def multiply(self, multiplier: int) -> 'Money':
        return Money(self.amount * multiplier, self.currency)

@dataclass(frozen=True)
class CustomerId:
    """고객 ID를 표현하는 밸류 오브젝트"""
    value: UUID
    
    @classmethod
    def generate(cls) -> 'CustomerId':
        return cls(uuid4())

@dataclass(frozen=True)
class Address:
    """배송 주소를 표현하는 밸류 오브젝트"""
    street: str
    city: str
    postal_code: str
    country: str = "KR"
    
    def __post_init__(self):
        if not self.street or not self.city or not self.postal_code:
            raise ValueError("주소 정보가 불완전합니다")

# 도메인 이벤트 (Domain Events)
@dataclass
class DomainEvent:
    """도메인 이벤트 기본 클래스"""
    event_id: UUID
    occurred_at: datetime
    aggregate_id: UUID

@dataclass
class OrderCreated(DomainEvent):
    """주문 생성 이벤트"""
    customer_id: UUID
    total_amount: Money

@dataclass
class OrderItemAdded(DomainEvent):
    """주문 항목 추가 이벤트"""
    product_id: UUID
    quantity: int
    price: Money

# 열거형 (Enums)
class OrderStatus(Enum):
    DRAFT = "DRAFT"
    CONFIRMED = "CONFIRMED"
    PAID = "PAID"
    SHIPPED = "SHIPPED"
    DELIVERED = "DELIVERED"
    CANCELLED = "CANCELLED"

# 엔티티 (Entities)
class OrderItem:
    """주문 항목 엔티티"""
    def __init__(self, product_id: UUID, quantity: int, unit_price: Money):
        if quantity <= 0:
            raise ValueError("수량은 0보다 커야 합니다")
        if unit_price.amount <= 0:
            raise ValueError("가격은 0보다 커야 합니다")
            
        self.product_id = product_id
        self.quantity = quantity
        self.unit_price = unit_price
    
    def calculate_total(self) -> Money:
        """항목별 총액 계산"""
        return self.unit_price.multiply(self.quantity)
    
    def change_quantity(self, new_quantity: int):
        """수량 변경"""
        if new_quantity <= 0:
            raise ValueError("수량은 0보다 커야 합니다")
        self.quantity = new_quantity

class Order:
    """주문 애그리거트 루트"""
    def __init__(self, customer_id: CustomerId, shipping_address: Address):
        self.id = uuid4()
        self.customer_id = customer_id
        self.shipping_address = shipping_address
        self.status = OrderStatus.DRAFT
        self.created_at = datetime.now()
        self.items: List[OrderItem] = []
        self._domain_events: List[DomainEvent] = []
    
    def add_item(self, product_id: UUID, quantity: int, unit_price: Money):
        """주문 항목 추가"""
        if self.status != OrderStatus.DRAFT:
            raise ValueError("확정된 주문은 수정할 수 없습니다")
        
        # 기존 항목 확인
        existing_item = self._find_item(product_id)
        if existing_item:
            existing_item.change_quantity(existing_item.quantity + quantity)
        else:
            item = OrderItem(product_id, quantity, unit_price)
            self.items.append(item)
        
        # 도메인 이벤트 추가
        self._add_domain_event(OrderItemAdded(
            event_id=uuid4(),
            occurred_at=datetime.now(),
            aggregate_id=self.id,
            product_id=product_id,
            quantity=quantity,
            price=unit_price
        ))
    
    def remove_item(self, product_id: UUID):
        """주문 항목 제거"""
        if self.status != OrderStatus.DRAFT:
            raise ValueError("확정된 주문은 수정할 수 없습니다")
        
        self.items = [item for item in self.items if item.product_id != product_id]
    
    def calculate_total(self) -> Money:
        """주문 총액 계산"""
        if not self.items:
            return Money(Decimal('0'))
        
        total = self.items[0].calculate_total()
        for item in self.items[1:]:
            total = total.add(item.calculate_total())
        return total
    
    def confirm_order(self):
        """주문 확정"""
        if self.status != OrderStatus.DRAFT:
            raise ValueError("이미 확정된 주문입니다")
        if not self.items:
            raise ValueError("주문 항목이 없습니다")
        
        self.status = OrderStatus.CONFIRMED
        
        # 주문 생성 이벤트 발행
        self._add_domain_event(OrderCreated(
            event_id=uuid4(),
            occurred_at=datetime.now(),
            aggregate_id=self.id,
            customer_id=self.customer_id.value,
            total_amount=self.calculate_total()
        ))
    
    def ship_order(self):
        """주문 배송"""
        if self.status != OrderStatus.PAID:
            raise ValueError("결제된 주문만 배송 가능합니다")
        self.status = OrderStatus.SHIPPED
    
    def _find_item(self, product_id: UUID) -> Optional[OrderItem]:
        """상품 ID로 주문 항목 찾기"""
        return next((item for item in self.items if item.product_id == product_id), None)
    
    def _add_domain_event(self, event: DomainEvent):
        """도메인 이벤트 추가"""
        self._domain_events.append(event)
    
    def get_domain_events(self) -> List[DomainEvent]:
        """도메인 이벤트 조회"""
        return self._domain_events.copy()
    
    def clear_domain_events(self):
        """도메인 이벤트 초기화"""
        self._domain_events.clear()

# 도메인 서비스 (Domain Services)
class OrderDomainService:
    """주문 도메인 서비스"""
    
    @staticmethod
    def can_apply_discount(order: Order, discount_percentage: Decimal) -> bool:
        """할인 적용 가능 여부 판단"""
        return (order.status == OrderStatus.DRAFT and 
                order.calculate_total().amount >= Decimal('100000') and
                discount_percentage <= Decimal('0.3'))
    
    @staticmethod
    def calculate_shipping_fee(order: Order) -> Money:
        """배송료 계산"""
        total = order.calculate_total()
        if total.amount >= Decimal('50000'):
            return Money(Decimal('0'))  # 무료배송
        return Money(Decimal('3000'))   # 기본 배송료

# 리포지토리 인터페이스 (Repository Interface)
class OrderRepository(ABC):
    """주문 리포지토리 인터페이스"""
    
    @abstractmethod
    def save(self, order: Order) -> None:
        """주문 저장"""
        pass
    
    @abstractmethod
    def find_by_id(self, order_id: UUID) -> Optional[Order]:
        """ID로 주문 조회"""
        pass
    
    @abstractmethod
    def find_by_customer_id(self, customer_id: CustomerId) -> List[Order]:
        """고객 ID로 주문 목록 조회"""
        pass

# ============================================================================
# 2. 응용 계층 (Application Layer)
# ============================================================================

@dataclass
class CreateOrderCommand:
    """주문 생성 커맨드"""
    customer_id: UUID
    shipping_address: Address
    items: List[dict]  # [{"product_id": UUID, "quantity": int, "unit_price": Decimal}]

@dataclass
class AddOrderItemCommand:
    """주문 항목 추가 커맨드"""
    order_id: UUID
    product_id: UUID
    quantity: int
    unit_price: Decimal

class OrderApplicationService:
    """주문 응용 서비스"""
    
    def __init__(self, order_repository: OrderRepository):
        self.order_repository = order_repository
    
    def create_order(self, command: CreateOrderCommand) -> UUID:
        """주문 생성"""
        customer_id = CustomerId(command.customer_id)
        order = Order(customer_id, command.shipping_address)
        
        # 주문 항목 추가
        for item_data in command.items:
            order.add_item(
                product_id=item_data["product_id"],
                quantity=item_data["quantity"],
                unit_price=Money(Decimal(str(item_data["unit_price"])))
            )
        
        self.order_repository.save(order)
        return order.id
    
    def add_order_item(self, command: AddOrderItemCommand):
        """주문 항목 추가"""
        order = self.order_repository.find_by_id(command.order_id)
        if not order:
            raise ValueError("주문을 찾을 수 없습니다")
        
        order.add_item(
            command.product_id,
            command.quantity,
            Money(Decimal(str(command.unit_price)))
        )
        
        self.order_repository.save(order)
    
    def confirm_order(self, order_id: UUID):
        """주문 확정"""
        order = self.order_repository.find_by_id(order_id)
        if not order:
            raise ValueError("주문을 찾을 수 없습니다")
        
        order.confirm_order()
        self.order_repository.save(order)
        
        # 이벤트 처리 (실제로는 이벤트 버스로 발행)
        events = order.get_domain_events()
        for event in events:
            print(f"이벤트 발행: {event}")
        order.clear_domain_events()

# ============================================================================
# 3. 인프라스트럭처 계층 (Infrastructure Layer)
# ============================================================================

class InMemoryOrderRepository(OrderRepository):
    """인메모리 주문 리포지토리 구현체"""
    
    def __init__(self):
        self._orders: dict[UUID, Order] = {}
    
    def save(self, order: Order) -> None:
        """주문 저장"""
        self._orders[order.id] = order
    
    def find_by_id(self, order_id: UUID) -> Optional[Order]:
        """ID로 주문 조회"""
        return self._orders.get(order_id)
    
    def find_by_customer_id(self, customer_id: CustomerId) -> List[Order]:
        """고객 ID로 주문 목록 조회"""
        return [order for order in self._orders.values() 
                if order.customer_id == customer_id]

# ============================================================================
# 4. 프레젠테이션 계층 (Presentation Layer)
# ============================================================================

class OrderController:
    """주문 컨트롤러"""
    
    def __init__(self, order_service: OrderApplicationService):
        self.order_service = order_service
    
    def create_order(self, request_data: dict) -> dict:
        """주문 생성 API"""
        try:
            command = CreateOrderCommand(
                customer_id=UUID(request_data["customer_id"]),
                shipping_address=Address(**request_data["shipping_address"]),
                items=request_data["items"]
            )
            
            order_id = self.order_service.create_order(command)
            return {"success": True, "order_id": str(order_id)}
            
        except Exception as e:
            return {"success": False, "error": str(e)}

# ============================================================================
# 5. 사용 예시
# ============================================================================

if __name__ == "__main__":
    # 의존성 주입 설정
    repository = InMemoryOrderRepository()
    service = OrderApplicationService(repository)
    controller = OrderController(service)
    
    # 주문 생성 예시
    request = {
        "customer_id": str(uuid4()),
        "shipping_address": {
            "street": "강남대로 123",
            "city": "서울",
            "postal_code": "06234"
        },
        "items": [
            {
                "product_id": uuid4(),
                "quantity": 2,
                "unit_price": "25000"
            },
            {
                "product_id": uuid4(),
                "quantity": 1,
                "unit_price": "15000"
            }
        ]
    }
    
    result = controller.create_order(request)
    print("주문 생성 결과:", result)
    
    if result["success"]:
        order_id = UUID(result["order_id"])
        
        # 주문 확정
        service.confirm_order(order_id)
        print("주문이 확정되었습니다.")
        
        # 주문 조회
        order = repository.find_by_id(order_id)
        if order:
            print(f"주문 상태: {order.status}")
            print(f"주문 총액: {order.calculate_total().amount} {order.calculate_total().currency}")

도메인 계층

구성 요소유형핵심 역할
OrderAggregate Root주문 생명주기 관리 및 비즈니스 규칙 강제
OrderItemEntity개별 상품 주문 정보 관리
Money, CustomerId, AddressValue Objects도메인 개념의 타입 안전성 보장
OrderStatusEnumeration주문 상태의 명확한 정의
DomainEventEvent도메인에서 발생하는 중요 사건 표현
OrderDomainServiceDomain Service복잡한 도메인 로직 처리
OrderRepositoryRepository 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

응용/프레젠테이션/인프라 계층

구성 요소유형핵심 역할
OrderControllerPresentationHTTP 요청/응답 처리
OrderApplicationServiceApplication유스케이스 조정 및 트랜잭션 관리
Command ObjectsApplication요청 데이터 캡슐화
InMemoryOrderRepositoryInfrastructure실제 저장소 구현체
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

데이터 플로우

  1. 요청 처리: OrderControllerOrderApplicationService
  2. 커맨드 변환: HTTP Request → Command Object
  3. 도메인 로직 실행: Application Service → Domain Objects
  4. 데이터 영속화: Application Service → Repository
  5. 응답 생성: 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)
데이터 암호화민감 정보 보호개인정보, 결제 정보 암호화
감사 로그중요 작업 기록주문 생성, 수정, 취소 이력 추적
모니터링 및 관찰 가능성
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 메트릭스 (Metrics) 예시
@dataclass
class OrderMetrics:
    """주문 관련 메트릭스"""
    orders_created_total: int
    orders_confirmed_total: int
    orders_cancelled_total: int
    average_order_value: Decimal
    order_processing_duration: float

# 분산 추적 (Distributed Tracing) 예시
@dataclass
class TraceContext:
    """분산 추적 컨텍스트"""
    trace_id: str
    span_id: str
    parent_span_id: Optional[str]
    operation_name: str

이러한 확장을 통해 더욱 견고하고 확장 가능한 도메인 주도 설계 기반 시스템을 구축할 수 있다. 각 패턴과 구성 요소는 시스템의 복잡성과 요구사항에 따라 단계적으로 도입하는 것을 권장한다.

장점

분류항목설명
설계 중심비즈니스 중심 설계도메인 모델이 비즈니스 로직을 정확히 반영하여 소프트웨어와 비즈니스의 정렬성 향상
커뮤니케이션유비쿼터스 언어도메인 전문가와 개발자가 공유하는 공통 언어로 일관된 커뮤니케이션 유지
구조화바운디드 컨텍스트도메인 경계를 명확히 구분하여 복잡성 분리 및 모듈화 가능
유지보수성높은 응집도 / 낮은 결합도관련 도메인 로직의 집중화 및 컨텍스트 간 최소 의존성으로 변경 용이
테스트 용이성테스트 편의성도메인 로직이 독립적으로 유지되어 단위 테스트 및 유지보수 간편
확장성독립적 배포 및 진화 구조마이크로서비스 및 독립 배포에 적합한 구조로, 확장성과 팀 분산 개발에 유리
복잡성 관리복잡한 비즈니스 모델 캡슐화핵심 도메인을 중심으로 복잡한 로직을 체계적으로 정리하여 유지보수성 및 설계 품질 향상
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 원칙 적용
해결 방법:

 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
# 문제 있는 코드 (Anemic)
class Order:
    def __init__(self):
        self.total = 0
        self.items = []
    
    def get_total(self):
        return self.total
    
    def set_total(self, total):
        self.total = total

# 개선된 코드 (Rich Domain Model)
class Order:
    def __init__(self):
        self._total = 0
        self._items = []
    
    def add_item(self, item):
        self._items.append(item)
        self._calculate_total()
    
    def _calculate_total(self):
        self._total = sum(item.get_subtotal() for item in self._items)
    
    @property
    def total(self):
        return self._total
Repository 패턴 오용

원인: Repository 를 단순 DAO 로 사용하거나 비즈니스 로직 포함
영향: 관심사 분리 원칙 위배, 테스트 어려움
탐지 및 진단: Repository 에 비즈니스 로직이나 복잡한 쿼리 로직 존재 여부 확인
예방 방법: Repository 인터페이스를 도메인 관점에서 정의
해결 방법:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 잘못된 예시
class CustomerRepository:
    def find_customers_with_high_value_orders(self, threshold):
        # 복잡한 비즈니스 로직이 Repository에 포함됨
        pass

# 올바른 예시
class CustomerRepository:
    def find_by_specification(self, specification):
        # 도메인 사양 객체를 통한 검색
        pass

class HighValueCustomerSpecification:
    def __init__(self, threshold):
        self.threshold = threshold
    
    def is_satisfied_by(self, customer):
        return customer.get_total_order_value() > self.threshold

도전 과제 및 해결책

조직 및 협업 과제

도전 과제원인영향예방 전략해결 방법
도메인 전문가 협업 부족시간 제약, 용어 차이정보 단절, 설계 오류정기적 이벤트 스토밍, 유비쿼터스 언어 정착크로스 펑셔널 팀 구성, 페어 설계
DDD 도입 저항기존 관행, 학습 부담도입 지연, 낮은 품질교육 세션, 점진적 도입성공 사례 공유, 실험적 적용
팀 간 도메인 이해 격차용어 정의 불일치소통 오류용어사전 작성, 모델 리뷰코드 리뷰 시 도메인 개념 반영
조직과 컨텍스트 불일치조직 구조와 도메인 불일치Context DriftTeam Topologies 반영팀 구조/컨텍스트 재정렬

설계 및 모델링 과제

도전 과제원인영향예방 전략해결 방법
컨텍스트 경계 설정 어려움개념적 추상성복잡도 증가이벤트 스토밍, 용어 기반 구분조직 구조 연계한 Context Map 설계
도메인 모델 설계 난이도복잡한 비즈니스 로직추상화 실패핵심 도메인부터 모델링반복적 리팩토링 및 모델링 세션 활용
유비쿼터스 언어 유지 어려움문서화 부족소통 오류코드 기반 언어 사용, 정기 워크숍정기 리뷰 및 자동화 도구 적용

기술 및 아키텍처 과제

도전 과제원인영향예방 전략해결 방법
이벤트 아키텍처 복잡성멱등성, 순서 문제디버깅 어려움이벤트 스키마 명세화메시지 추적 도구, 멱등 처리 적용
데이터 일관성 확보트랜잭션 경계 넘는 연산일관성 오류Saga, Outbox, 보상 트랜잭션분산 트레이싱 기반 설계
CQRS/Event Sourcing 과도 적용모든 도메인에 적용설계 오버헤드복잡도 기준 선별 도입Hybrid CQRS 전략 적용
이벤트/모델 버전 관리 문제계약 변경 시 장애서비스 중단명세 기반 계약 테스트Event Versioning, Schema Registry
설계 문서 부족코드 중심 개발설계 의도 손실Living Docs 도입ADR, 자동화 문서 시스템 활용

레거시 통합 과제

도전 과제원인영향예방 전략해결 방법
마이그레이션 복잡성기술 부채, 데이터 종속전환 비용 증가점진적 전환, ACL 활용Strangler Fig 패턴, 리팩터링 적용
데이터 정합성 문제컨텍스트 간 중복 저장불일치 및 오류이벤트 기반 통합동기 - 비동기 분리, Context Mapping 활용

운영 및 테스트 과제

도전 과제원인영향예방 전략해결 방법
이벤트 호환성 문제이벤트 변경소비자 장애명시적 버전 관리하위 호환 이벤트 설계, API Gateway 활용
모니터링/추적 부재비동기 구조장애 진단 어려움도메인 이벤트 기반 KPIDistributed 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) 들의 이력으로 저장하고, 현재 상태는 이 이벤트들을 순차적으로 리플레이하여 생성하는 방식이다.

장점:

단점 및 주의점:

Clean Architecture 와 DDD 결합

개념 설명:
Clean Architecture 는 계층 구조를 중심으로 책임을 분리하며, 내부 (core) 와 외부 (infrastructure) 를 명확히 구분한다. DDD 의 도메인 계층은 가장 중심 (Core Domain Layer) 에 위치한다.

계층 구조:

1
2
3
4
5
6
7
8
9
┌─────────────────────────────┐
│        External (UI/API)    │
├─────────────────────────────┤
│    Application Layer        │
├─────────────────────────────┤
│    Domain Layer (DDD 핵심)  │
├─────────────────────────────┤
│    Infrastructure Layer     │
└─────────────────────────────┘
계층설명
Application Layer서비스 오케스트레이션, 유스케이스 구현
Domain Layer엔티티, 애그리거트, 도메인 서비스 등
InfrastructureDB, 외부 시스템, 리포지토리 구현 등

실무 프로젝트 확장 전략 요약

전략목적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. 도메인 중심 분석

도구 추천:

2. 기존 코드 구조 정리
리팩토링 전리팩토링 후 (계층화)
ControllerController (UI Layer)
Service (비대해짐)Application Service
DAO, DTODomain Entity, Repository
3. 전술적 설계 도입
도메인 구성요소적용 기준
엔티티 (Entity)고유 ID 가 존재하고 상태 변경이 중요한 객체
값 객체 (Value Object)동일성보다 속성 자체가 중요한 불변 객체
애그리거트 (Aggregate)일관성을 유지해야 할 객체 묶음
도메인 서비스도메인 객체에 포함되지 않는 로직

예시:

1
2
3
4
5
6
7
# 값 객체 예시 (Python)
@dataclass(frozen=True)
class Email:
    value: str
    def __post_init__(self):
        if "@" not in self.value:
            raise ValueError("Invalid email")
4. 도메인 계층 중심 리팩토링
5.응용 계층 (Application Layer) 도입
1
2
3
4
5
6
7
8
9
public class PlaceOrderService {
    @Transactional
    public OrderId place(OrderRequest request) {
        Customer customer = customerRepository.findById(request.customerId());
        Order order = customer.placeOrder(request.items());
        orderRepository.save(order);
        return order.getId();
    }
}
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 + DDDCI/CD, Observability 통합자동화된 도메인 중심 운영 구조 설계
Team Topologies팀 구조 ↔ 컨텍스트 정렬팀 조직을 도메인 경계와 일치시킴
Legacy MigrationStrangler 패턴, 점진적 전환레거시 시스템을 도메인 아키텍처로 이동
확장 응용Agile 전략 - 전술 순환전술적 피드백 기반 전략 재설계반복적 모델 개선과 커뮤니케이션 강화
UX 전략과 전술도메인 기반 UI 리디자인사용자 경험에 도메인 지식 반영
서브도메인 진화 사례Generic → Core 진화기능적 중요도 변화에 따른 재분류 가능성
Data Mesh도메인 소유 데이터 아키텍처데이터 팀이 도메인 중심으로 독립 운영 가능

주제와 관련하여 반드시 학습해야 할 내용

카테고리주제핵심 항목설명
기초 원칙하위 도메인 식별 (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 PatternisSatisfiedBy(), AND/OR 조합조건 로직 재사용 및 필터링 구조화
Domain Event 설계이벤트 발행/구독, Side Effect 분리상태 변화 기록 및 통합 트리거
도메인 테스트 전략단위/통합 테스트트랜잭션, 규칙 검증 및 코드 품질 확보
Anemic Domain Model 제거비즈니스 로직 분산 방지풍부한 도메인 모델 구현 유도
이벤트 기반 설계Event-Driven Integration도메인 간 비동기 통합이벤트 기반 메시징 아키텍처 구현
Event Sourcing + Snapshotting상태 복원 최적화저장 비용 및 성능 균형 확보
Saga 패턴 (보상 트랜잭션)트랜잭션 분산 및 실패 처리마이크로서비스 간 데이터 정합성 확보
아키텍처 패턴CQRS (명령/조회 분리)Command ↔ Query 분리쓰기/읽기 분리 통한 성능/유지보수 개선
Hexagonal ArchitecturePort-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)유비쿼터스 언어 기반의 테스트 자동화 및 시나리오 중심 개발 기법

참고 및 출처

개요 및 핵심 개념

도서 및 저자 원문

Strategic Design 관련 자료

Tactical Design 관련 자료

통합 및 사례 중심 자료

전략 - 전술 균형에 관한 추가 해설