Tactical Design(전술적 설계)
Tactical DDD 는 전략적 설계로 정의된 Bounded Context 내에서 도메인 모델을 실제 코드 구조로 구현하는 단계이다. 이 단계에서는 Entity(엔터티), Value Object(값 객체), Aggregate(애그리거트), Domain Service(도메인 서비스), Repository(저장소), Domain Event(도메인 이벤트) 같은 전술 패턴을 적용하여 강결합된 도메인 논리를 비즈니스 규칙에 맞게 내부적으로 캡슐화하고, 데이터 일관성을 유지한다. 또한 해당 패턴들로 모듈 경계를 명확히 하고 객체 간 관계를 구조화하여 유지보수성과 확장성을 확보한다.
핵심 개념
개념 | 정의 | 역할 | 실무 구현 요소 |
---|---|---|---|
Entity (엔터티) | 고유한 식별자를 가지며 상태와 생명 주기를 관리하는 객체 | 동일성은 ID 로 판단, 상태 변경 가능 | - ID 필드 (UUID 등) - 상태 변경 메서드 ( changeXXX )- 비즈니스 불변식 검증 |
Value Object (값 객체) | 식별자 없이 속성 값으로 동일성 판단, 불변 객체 | 도메인 개념 표현, 엔터티의 속성 구성 | - 생성자 내 유효성/불변성 검증 - equals , hashCode 구현- 수정 불가 구조 |
Aggregate & Aggregate Root | 일관성을 유지하는 객체 클러스터, 외부 진입은 루트만 허용 | 트랜잭션 범위 및 불변성 보장 단위 | - 내부 참조는 루트 통해서만 - 루트에 상태 변경 메서드 집중 - 비즈니스 규칙 캡슐화 |
Domain Service | 상태 없는 객체, 엔터티/VO 에 귀속되지 않는 도메인 로직 수행 | 도메인 객체 간 협력 로직 수행 | - 명령형 도메인 메서드 정의 - 도메인 계층에 위치 - 외부 의존 최소화 |
Repository | Aggregate 단위의 영속성 추상화 | 도메인 로직과 영속 기술 분리 | - save , findById , delete 등- ORM 연동 (SQLAlchemy, TypeORM 등) - 인터페이스 분리 |
Domain Event | 상태 변화 알림 및 비동기 트리거 객체 | 사이드이펙트 분리 및 이벤트 기반 연동 | - 이벤트 클래스 (OrderCreatedEvent 등)- 퍼블리셔/리스너 구조 - 후속 처리 핸들러 구현 |
Factory | 복잡한 객체 생성을 외부로 분리하는 패턴 | 생성 책임 분리, 일관된 생성 방식 제공 | - 정적 팩토리 메서드 (create )- 빌더 패턴 적용 - 생성 과정 불변성 보장 |
Specification | 비즈니스 규칙을 객체화해 재사용 가능하게 함 | 조건 조합 및 검증 로직 분리 | - isSatisfiedBy(entity) 메서드- AND/OR/NOT 조합 지원 - 리포지토리 조건 필터로 활용 |
Ubiquitous Language | 도메인 전문가와 개발자 간 공통 언어 | 용어 명확화 및 코드 일관성 강화 | - 클래스/메서드에 도메인 용어 반영 - 주석 및 문서화에 반영 |
Bounded Context | 도메인 모델이 유효한 논리적 경계 | 모델 중복/충돌 방지, 통합 전략 기반 | - 모듈/마이크로서비스 경계 설정 - Context Mapping 활용 (ACL, Shared Kernel 등) |
추가 실무 고려 요소
요소 | 설명 |
---|---|
트랜잭션 경계 | Aggregate 단위에서 관리. DB 트랜잭션과 일치시켜 데이터 정합성 보장. |
계층 분리 (Layered Architecture) | Domain / Application / Infrastructure / Interface 계층 분리 |
의존성 역전 (Dependency Inversion) | 도메인이 외부 계층에 의존하지 않도록 인터페이스 추상화 |
불변성 보장 (Invariant Enforcement) | Entity, Value Object, Aggregate 내부에서 규칙을 보호하고 검증 |
목적 및 필요성
주요 목적:
- 비즈니스 복잡성 관리: 복잡한 비즈니스 규칙을 명확하게 표현
- 유지보수성 향상: 변경 요구사항에 유연하게 대응
- 도메인 지식 보존: 비즈니스 로직을 코드에 명시적으로 표현
- 협업 개선: 비즈니스 전문가와 개발자 간 소통 원활화
필요성:
- 복잡한 비즈니스 도메인의 정확한 모델링
- 장기적인 소프트웨어 품질 보장
- 비즈니스 변화에 대한 적응력 확보
- 개발팀과 비즈니스팀 간의 이해 격차 해소
주요 기능 및 역할
- 도메인 모델 구체화: 추상적인 비즈니스 개념을 구현 가능한 코드로 변환
- 비즈니스 규칙 캡슐화: 도메인 로직을 적절한 객체에 배치
- 데이터 일관성 보장: Aggregate 를 통한 트랜잭션 경계 관리
- 관심사 분리: 도메인 로직과 기술적 관심사의 명확한 분리
특징
- 코드 중심: 실제 구현에 초점.
- 선언적 모델링: 비즈니스 의도를 명확하게 표현
- 계층화된 아키텍처: 각 계층의 책임 명확화
- 풍부한 도메인 모델: 데이터와 행동을 함께 캡슐화
- 패턴 기반: Aggregate, Entity, Value Object 등 DDD 패턴 활용.
- 일관성 및 무결성: 도메인 모델의 일관성과 무결성 유지.
- 테스트 용이성: 비즈니스 로직의 독립적 테스트 가능
핵심 원칙
전술 설계 (Tactical Design) 는 개념적 도메인 모델을 코드 수준으로 구체화하는 단계로, 비즈니스 요구사항을 도메인 모델로 추상화하고, 이를 기반으로 소프트웨어를 구현하며, 구현 결과에 대한 피드백을 통해 모델을 지속적으로 개선하는 것이다.
Tactical Design 은 모델 주도 설계의 실천적 구현 단계로, 도출된 도메인 모델을 소프트웨어 구조와 코드로 구체화한다. 즉, 모델 주도 설계가 " 무엇을 모델링할 것인가 " 에 집중한다면, Tactical Design 은 " 어떻게 모델을 코드로 구현할 것인가 " 에 집중한다.
Bounded Context 내에서 실행
- 전술적 설계는 전략적 설계로 이루어진 경계 (
bounded context
) 내부에서, 도메인 모델을 세밀하게 구성하고 구현하는 단계이다. - 각 컨텍스트는 독립된 모델과 언어 (Ubiquitous Language) 를 갖고, 모델이 내부적으로 일관성을 유지해야 한다.
- 전술적 설계는 전략적 설계로 이루어진 경계 (
도메인 주체의 실체화
- Entity, Value Object, Aggregate 같은 DDD 빌딩 블록을 코드로 구체화한다.
- Entity: 고유 ID 기반의 라이프사이클을 갖는 객체.
- Value Object: 불변이며 값 자체로 정체성이 결정된다.
- Aggregate: 하나의 루트 엔티티 주도 하에 관련 객체 묶음 구성, 상태 불변성과 비즈니스 규칙 인한 일관성 보장.
도메인 행동 캡슐화
- 중요 비즈니스 로직은 엔티티나 값 객체 내부 메서드로 구현하고, 상태 변경은 도메인 객체를 통해 수행한다.
- 이를 통해 애플리케이션 서비스나 UI 레이어가 아닌 도메인 계층에 로직이 집중된다.
인프라와 분리된 저장소 인터페이스
- Repository 패턴을 통해 도메인 모델의 저장·조회 책임을 추상화하며, 비즈니스 계층과 데이터 영속화의 종속성을 방지.
무상태성과 명확한 단일 책임
- Domain Service: Entity 에 속하지 않는 비즈니스 로직을 수행하며, 상태를 유지하지 않고 명확한 비즈니스 역할만 수행한다.
- Factory: 복잡한 도메인 객체 생성 절차를 분리하여 코드 양방향성을 줄이고 SRP(Single Responsibility Principle) 준수.
도메인 이벤트를 통한 의사소통
- Domain Event는 시스템 내 중요한 상태 변화를 캡쳐하여 느슨한 결합 방식으로 구성요소 간 상호작용을 원활히 한다.
모듈화 및 구조 정리
- Module 또는 패키지를 통해 관련 도메인 개념 (Entity, VO 등) 을 비즈니스 단위로 그룹화하며, 탐색성과 유지보수성을 향상시킨다.
반복적 모델 - 코드 피드백 루프
- 전술 설계는 반복적인 탐색–구현–리팩터링 사이클을 포함하며, 이는 모델과 코드의 동기화를 유지해 도메인 변경을 빠르게 수용할 수 있게 한다.
graph TD A[비즈니스 요구사항] --> B[도메인 모델] B --> C[구현 코드] C --> D[피드백] D --> A B --> E[Entity] B --> F[Value Object] B --> G[Aggregate] B --> H[Domain Service]
- Aggregate 패턴: 연관된 객체를 하나의 단위로 묶어 일관성 관리.
- Entity, Value Object: 도메인 객체의 식별 및 불변성 관리.
- Repository, Domain Service: 데이터 접근 및 복잡한 도메인 로직 처리.
- 계층화 아키텍처: 도메인, 애플리케이션, 인프라 계층 분리.
구조 및 아키텍처
graph TB %% 계층별 그룹 정의 subgraph "Application Layer" AL[Application Service] AH[Application Handler] end subgraph "Domain Layer" AG[Aggregate Root] E[Entity] VO[Value Object] DS[Domain Service] REPOI[Repository Interface] DE[Domain Event] F[Factory] end subgraph "Infrastructure Layer" R[Repository Implementation] DB[(Database)] EH[Event Handler] ES[External System] end %% 흐름 정의 AL --> AG AL --> DS AL --> REPOI AH --> DS AG --> E AG --> VO AG --> DE F --> AG REPOI --> R R --> DB EH --> DE ES --> DS %% 느슨한 결합 표현 AL -.-> DE AG -.-> REPOI REPOI -.-> R
핵심 도메인 빌딩 블록
핵심 도메인 빌딩 블록은 도메인 주도 설계 (DDD, Domain-Driven Design) 에서 복잡한 비즈니스 도메인을 효과적으로 소프트웨어로 구현하기 위해 사용하는 전술적 설계 (Tactical Design) 의 주요 구성 요소를 의미한다.
이 빌딩 블록들은 도메인 모델을 코드로 구체화할 때 일관성과 유지보수성을 높여주며, 도메인 로직의 명확한 분리와 재사용성을 보장한다.
핵심 도메인 빌딩 블록의 목적은 다음과 같다:
- 복잡도 관리: 도메인 모델의 복잡도를 낮추고, 구현에 대한 명확한 가이드를 제공한다.
- 도메인 로직의 격리: 도메인 로직을 외부 기술 요소 (프레임워크, DB 등) 로부터 분리하여, 비즈니스 요구사항 변화에 유연하게 대응할 수 있다.
- 유비쿼터스 언어 지원: 도메인 전문가와 개발자가 동일한 언어로 소통할 수 있도록 모델과 코드를 일치시킨다.
- 일관성 및 재사용성: 각 빌딩 블록은 명확한 역할과 책임을 가지며, 재사용 가능한 구조를 제공한다.
구분 | 구성요소 | 기능 | 역할 | 특징 |
---|---|---|---|---|
핵심 | Entity (엔티티) | 고유 식별자를 기반으로 상태를 가지며 변경 가능 | 도메인 개념의 실체화, 상태 추적 및 생명주기 관리 | ID 기반 동일성, 가변성 |
Value Object (값 객체) | 불변의 값으로 도메인 속성 표현 | 동일성 판단, 개념 명확화, 사이드 이펙트 없는 사용 | 불변성, 값 비교 기반 동일성 | |
Aggregate Root (집합체 루트) | 관련 엔티티와 값 객체 묶음의 트랜잭션 및 일관성 경계 책임 | Aggregate 내부 일관성 보장, 외부 노출 지점 | 루트 외부 접근 제한, 트랜잭션 단위 관리 | |
Repository (저장소 인터페이스) | 도메인 객체의 저장/조회 인터페이스 | 도메인 계층과 인프라 계층 분리, 데이터 접근 추상화 | 인터페이스 기반, 테스트 대체 용이 | |
보완 | Domain Service (도메인 서비스) | 여러 도메인 객체에 걸친 도메인 연산 처리 | Entity 에 귀속되지 않는 핵심 비즈니스 로직 수행 | 무상태, SRP(단일 책임 원칙) 준수 |
Factory (팩토리) | 복잡한 객체 (예: Aggregate) 생성 책임 분리 | 도메인 객체 생성의 불변식 및 유효성 보장 | 생성 책임 위임, 도메인 초기화 캡슐화 | |
Domain Event (도메인 이벤트) | 도메인 내 중요한 사건을 객체로 표현 | 상태 변화의 표현, 모듈 간/시스템 간 비동기 통신 지원 | 이벤트 중심 구조, 느슨한 결합 |
Entity (엔티티)
엔티티는 도메인 내에서 고유 식별자 (예: order_id) 를 가지며, 상태와 생명주기를 관리하는 객체다.
비즈니스에서 중요한 개체 (예: 주문, 사용자 등) 를 표현하며, 동일한 속성을 가진 객체라도 식별자가 다르면 서로 다른 엔티티로 간주한다.
핵심 특성
- 고유 식별자 (Identity) 규칙: 엔티티는 반드시 고유 식별자를 가져야 하며, 동일한 식별자를 가지는 객체는 같은 엔티티로 간주한다.
- 상태 변화 및 생명주기 관리: 엔티티는 상태 변화 (생성, 수정, 삭제 등) 가 가능하며, 생명주기를 관리할 수 있어야 한다.
- 동등성 (Equality) 규칙: 두 엔티티의 동일성은 식별자 (ID) 로 판단한다.
- 도메인 개념의 실체화: 비즈니스에서 중요한 개체 (예: 주문, 사용자 등) 를 실체화한다.
코드 예시
- 주요 특징
- 식별자:
order_id
로 엔티티의 고유성을 보장한다. - 상태:
status
속성으로 주문의 상태를 관리한다. - 행동:
add_item()
메서드로 주문 항목을 추가하며, 엔티티의 상태 변화를 유발한다. - 초기화:
items
는 기본값을 빈 리스트로 설정하여, 주문 생성 시 항목이 없을 수도 있음을 반영한다.
- 식별자:
- 주요 특징
Value Object (값 객체)
- 값 객체는 식별자가 없고, 값 자체로 동일성을 판단하는 불변 (immutable) 객체로, DDD(도메인 주도 설계) 에서 중요한 빌딩 블록이다.
- 값 객체는 동일한 속성과 값을 가지면 서로 같은 것으로 간주하며, 변경이 불가능해야 한다.
- 대표적으로 주소 (Address), 금액 (Money), 기간 (Period) 등과 같이 속성의 조합이 의미를 가지는 개념에 사용된다.
- 핵심 특성
- 불변성 (Immutable) 규칙: 값 객체는 생성 후 내부 속성을 변경할 수 없어야 한다.
- 값 기반 동등성 (Value-based Equality): 동일한 속성 값을 가지면 같은 객체로 간주한다.
- 식별자 없음: 값 객체는 고유 식별자를 가지지 않는다.
- 사이드 이펙트 없는 사용: 값 객체는 상태 변경이 없으므로, 여러 곳에서 안전하게 공유할 수 있다.
코드 예시
- 특징
- 불변성 보장:
@dataclass(frozen=True)
를 사용해 객체 생성 후 속성 변경을 막아 불변성을 보장한다. - 동등성/해시 자동 구현: dataclass 의 frozen 옵션을 사용하면 값 기반 동등성 (
__eq__
) 과 해시 (__hash__
) 가 자동으로 구현된다. - 식별자 없음: 여전히 식별자가 없으며, 값 자체로 동일성을 판단한다.
- 타입 명확성:
Decimal
타입을 그대로 사용해 금액 연산의 정확성을 유지한다.
- 불변성 보장:
- 특징
Aggregate Root (집합체 루트)
- **Aggregate Root(집합체 루트)**는 DDD(도메인 주도 설계) 에서 연관된 엔티티와 값 객체를 하나의 일관성 있는 단위 (애그리거트) 로 묶고, 외부에서는 반드시 루트 엔티티 (집합체 루트) 를 통해서만 내부 객체에 접근하도록 하는 설계 원칙이다.
- 집합체 루트는 데이터 일관성, 트랜잭션 경계, 도메인 규칙의 중심 역할을 하며, 대표적인 예로 쇼핑카트, 주문, 청구 명세 등이 있다.
- 핵심 특성
- 일관성 경계 (Consistency Boundary) 규칙: 애그리거트 내부의 모든 객체는 일관성 있게 관리되어야 하며, 외부에서는 루트를 통해서만 접근한다.
- 트랜잭션 단위 관리: 애그리거트는 트랜잭션 경계로 사용되며, 애그리거트 내부의 모든 변경은 원자적으로 처리된다.
- 불변식 (Invariant) 보장: 애그리거트는 내부 객체 간의 비즈니스 규칙 (불변식) 을 항상 만족시켜야 한다.
- 외부 접근 제한: 애그리거트 외부에서는 내부 객체에 직접 접근할 수 없고, 반드시 루트를 통해서만 접근한다.
코드 예시
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
class ShoppingCart: def __init__(self, cart_id): self._cart_id = cart_id self._items = [] @property def cart_id(self): return self._cart_id @property def items(self): # 외부에서 직접 수정 불가, 복사본 반환 return list(self._items) def add_item(self, item): # 도메인 규칙(예: 중복 방지, 수량 제한 등) 추가 가능 self._items.append(item) def total(self): return sum(item.price * item.qty for item in self._items)
- 특징
- 캡슐화 강화: 내부 리스트 (
_items
) 를 외부에서 직접 수정할 수 없도록 보호한다. - 불변성 유지:
items
프로퍼티는 복사본을 반환해 외부에서 직접 리스트를 변경하지 못하게 한다. - 도메인 규칙 확장 용이:
add_item()
메서드에서 중복 방지, 수량 제한 등 도메인 규칙을 쉽게 추가할 수 있다. - 테스트 용이성: 총합 연산, 아이템 추가/삭제, 불변성 유지 등 다양한 테스트 시나리오를 명확히 검증할 수 있다.
- 캡슐화 강화: 내부 리스트 (
- 특징
Repository (저장소 인터페이스)
**Repository(저장소 인터페이스)**는 도메인 모델과 데이터 저장소 (DB, 메모리 등) 간의 의존성을 분리하는 패턴으로, 도메인 객체의 저장, 조회, 삭제 등 영속성 관련 작업을 추상화한다.
도메인 계층에서는 저장소의 구체적 구현 (예: DB, 파일, 메모리 등) 에 신경 쓰지 않고, 저장소 인터페이스를 통해 도메인 객체를 다룰 수 있다.
핵심 특성
- 도메인 - 인프라 분리 규칙: 도메인 계층과 인프라 계층 (DB, 파일 등) 을 분리하여, 도메인 계층이 인프라에 의존하지 않도록 한다.
- 추상화 (Interface) 기반 설계: 저장소는 인터페이스로 정의되어, 테스트 및 다양한 구현체 (메모리, DB 등) 로 대체 가능하다.
- 도메인 객체 영속성 관리: 도메인 객체의 저장, 조회, 삭제 등 영속성 관련 작업을 추상화한다.
- 테스트 대체 용이: 테스트 환경에서는 메모리 저장소 등으로 쉽게 대체할 수 있다.
구현 예시:
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
from abc import ABC, abstractmethod class AbstractOrderRepository(ABC): @abstractmethod def get(self, order_id): pass @abstractmethod def save(self, order): pass @abstractmethod def delete(self, order_id): pass class InMemoryOrderRepository(AbstractOrderRepository): def __init__(self): self._storage = {} def get(self, order_id): return self._storage.get(order_id) def save(self, order): self._storage[order.order_id] = order def delete(self, order_id): if order_id in self._storage: del self._storage[order_id]
- 특징
- 인터페이스 분리:
AbstractOrderRepository
를 통해 저장소 인터페이스와 구현체를 분리했다. - 확장성: InMemory, DB 등 다양한 저장소 구현체를 쉽게 교체할 수 있다.
- 삭제 기능 추가:
delete()
메서드로 저장소에서 객체를 삭제할 수 있다. - 테스트 용이성: InMemory 구현체로 단위 테스트가 쉽고, 실제 환경에서는 DB 구현체로 교체 가능하다.
- 유지보수성: 인터페이스 기반 설계로 코드의 유지보수와 확장성이 높아진다.
- 인터페이스 분리:
- 특징
Domain Service (도메인 서비스)
도메인 서비스는 특정 엔티티에 속하지 않는 도메인 로직을 담당하는 객체로, 여러 엔티티에 걸친 비즈니스 규칙이나 연산을 분리하여 관리하는 역할을 한다.
예를 들어, 배송료 계산, 보험료 산정, 세금 계산 등 복잡한 비즈니스 로직이 엔티티에 속하지 않을 때 도메인 서비스로 구현한다.
핵심 특성
- 엔티티에 귀속되지 않는 로직: 여러 엔티티에 걸친 비즈니스 로직이나, 특정 엔티티에 속하지 않는 도메인 연산을 처리한다.
- 무상태 (Stateless) 규칙: 도메인 서비스는 상태를 가지지 않으며, 입력에 따라 결과만 반환한다.
- 단일 책임 원칙 (SRP) 준수: 한 도메인 서비스는 하나의 책임 (예: 배송료 계산, 세금 계산 등) 만을 가진다.
- 도메인 계층 내 위치: 도메인 서비스는 도메인 계층에 위치하며, 인프라 계층과는 분리되어야 한다.
구현 예시
1 2 3 4 5 6 7 8 9 10 11 12 13
from decimal import Decimal from typing import Any class ShippingCostService: def calculate(self, order: Any) -> Decimal: if not hasattr(order, "items"): raise ValueError("order 객체에 items 속성이 없습니다.") total_weight = Decimal("0") for item in order.items: if not hasattr(item, "weight"): raise ValueError("item에 weight 속성이 없습니다.") total_weight += Decimal(str(item.weight)) return total_weight * Decimal("0.5")
- 특징
- 타입 힌트 추가: 입력값과 반환값에 타입 힌트를 명시해 가독성과 안정성을 높였다.
- 입력값 검증:
order
와item
의 속성 존재 여부를 체크해 예외 상황에 안전하게 대응한다. - Decimal 변환: 금액 연산의 정확성을 위해 모든 weight 값을 Decimal 로 변환한다.
- 확장성: 향후 다양한 배송 정책 (예: 무게별, 지역별, 할인 등) 추가가 용이하다.
- 특징
Factory (팩토리)
팩토리 패턴은 객체의 생성 로직을 별도의 팩토리 객체에 위임하여, 클라이언트 코드가 객체 생성 방법에 의존하지 않도록 하는 설계 패턴이다.
복잡한 초기화, 다양한 생성 방식, 객체 생성의 일관성 보장, 테스트 용이성 등의 목적에서 활용된다.
DDD(도메인 주도 설계) 에서는 복잡한 애그리거트 (집합체) 나 도메인 객체의 생성을 팩토리로 분리해 도메인 모델의 순수성과 일관성을 높인다.
핵심 특성
- 생성 책임 분리 규칙: 복잡한 객체 (특히 애그리거트) 의 생성 로직을 팩토리로 분리하여, 클라이언트 코드가 생성 방법에 의존하지 않도록 한다.
- 불변식 (Invariant) 보장: 팩토리는 생성 시점에 도메인 규칙 (불변식) 을 반드시 만족시키는 객체를 생성해야 한다.
- 도메인 초기화 캡슐화: 객체 생성 및 초기화 로직을 팩토리 내부에 캡슐화한다.
- 테스트 용이성: 팩토리를 통해 생성된 객체는 초기 상태가 항상 일관되게 보장된다.
구현 예시
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
import uuid from typing import List class Order: def __init__(self, order_id: str, customer_id: str): self.order_id = order_id self.customer_id = customer_id self.items = [] def add_item(self, item): self.items.append(item) class OrderFactory: def create(self, customer_id: str, items: List) -> Order: order = Order(str(uuid.uuid4()), customer_id) for i in items: order.add_item(i) return order
- 특징
- 고객 ID 반영:
Order
객체에customer_id
가 포함되어, 도메인 요구사항을 충족한다. - 타입 힌트 추가: 입력값과 반환값에 타입 힌트를 명시해 가독성과 안정성을 높였다.
- 모듈 import 명확화:
uuid
모듈을 명시적으로 import 하여 코드의 완성도를 높였다. - 확장성: 향후 주문 생성 시 추가 비즈니스 규칙 (예: 아이템 유효성 검사, 주문 상태 초기화 등) 적용이 용이하다.
- 테스트 용이성: 생성된 주문의 ID, 고객 ID, 아이템 초기화 등 다양한 테스트 시나리오를 명확히 검증할 수 있다.
- 고객 ID 반영:
- 특징
Domain Event (도메인 이벤트)
- 도메인 이벤트는 도메인 내에서 발생하는 중요한 상태 변화를 명확하게 표현하는 객체로, 시스템의 반응성과 확장성을 높이기 위해 이벤트 기반 아키텍처에서 활용된다.
- 예시: 주문 배송 (OrderShipped), 계좌 입금 (AccountCredited), 사용자 회원 가입 (UserRegistered) 등 비즈니스적으로 의미 있는 사건을 나타낸다.
- 핵심 특성
- 불변성 (Immutable) 규칙: 도메인 이벤트는 생성 후 변경될 수 없어야 한다.
- 명확한 의미 전달: 이벤트는 도메인 내에서 발생한 중요한 상태 변화를 명확하게 표현해야 한다.
- 이벤트 중심 구조: 이벤트를 통해 모듈 간 또는 시스템 간 비동기 통신 및 확장성을 지원한다.
- 느슨한 결합 (Loose Coupling): 이벤트 발행자와 구독자는 서로 직접 참조하지 않고, 이벤트를 통해 간접적으로 통신한다.
구현 예시
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
from dataclasses import dataclass from datetime import datetime from typing import Any @dataclass(frozen=True) class DomainEvent: occurred_on: datetime event_type: str payload: Any # 사용 예시 order_shipped_event = DomainEvent( occurred_on=datetime(2025, 6, 19, 0, 0), event_type="OrderShipped", payload={"order_id": "ORD123"} )
- 특징
- 이벤트 타입 명시:
event_type
속성으로 이벤트의 종류를 명확히 구분할 수 있다. - 페이로드 확장성:
payload
에 다양한 정보를 딕셔너리 등으로 담을 수 있어, 여러 도메인 이벤트에 재사용이 가능하다. - 불변성 유지: 여전히
@dataclass(frozen=True)
로 불변성을 보장한다. - 직렬화 및 메시징 연계 용이: 메시지 큐, 이벤트 버스 등 외부 시스템과 연동 시 구조가 유연하다.
- 테스트 용이성: 이벤트 타입, 페이로드, 발생 시각 등 다양한 속성에 대한 단위 테스트가 가능하다.
- 이벤트 타입 명시:
- 특징
기타
구분 | 구성요소 | 기능 | 역할 | 특징 |
---|---|---|---|---|
인프라 | Repository Implementation (저장소 구현체) | 실제 영속성 (DB) 과의 연결을 담당하는 클래스 | 도메인 인터페이스를 구현하여 DB 접근 | 구현체 분리, Test Double 사용 가능 |
Database (데이터베이스) | 상태 영속성 저장소 | 도메인 객체의 영속 데이터 저장 | 관계형/NoSQL 모두 가능 | |
Event Handler (이벤트 핸들러) | Domain Event 를 구독하고 후속 처리 로직 수행 | 알림, 로깅, 비동기 프로세싱 등 외부 트리거 | 비동기 메시지 처리, 이벤트 기반 연동 | |
External System (외부 시스템) | 외부 API, 메시지 브로커, 타 시스템 등과 통합 | 도메인 서비스 또는 핸들러에서 호출되는 외부 구성 요소 | 외부 의존, 네트워크 경계 명확화 | |
응용 | Application Service (앱 서비스) | 유스케이스 단위의 비즈니스 흐름 조율 | 도메인 계층 조정, 트랜잭션 경계 제어 | 도메인 객체 직접 로직 수행하지 않음 |
Application Handler (앱 핸들러) | 명령/이벤트/요청을 처리하고 Application Service 호출 | 입력값 유효성 검사, 흐름 제어, 트리거 역할 | 경량 계층, 흐름 진입점 |
구현 기법
기법 | 정의 & 목적 | 주요 특징 | 대표 예시 |
---|---|---|---|
Guard / Assertion | 도메인 객체의 생성 및 변경 시 ** 불변식 (invariant)** 과 ** 사전조건 (precondition)** 을 코드에서 직접 검사 | - 오류를 빠르게 감지하고 - 일관성, 신뢰성 확보 | 수량 ≥ 1 검증, 이메일/우편번호 포맷 확인 |
Specification | 복잡한 비즈니스 규칙을 별도 객체로 추출하여 조합과 재사용이 가능하도록 구성 | - 규칙 조합 가능 (AND/OR/NOT) - 테스트 및 유지 보수 용이 | OrderEligibleSpec , UserActiveSpec |
Optimistic Locking | 버전 (version) 필드 기반으로 동시성 충돌을 감지 및 처리하는 낙관적 잠금 기법 | - 충돌 시 예외와 재시도 유도 - 데이터 일관성 유지 | JPA @Version , updated_at 기반 충돌 처리 |
CQRS | ** 읽기 (Query)** 와 쓰기 (Command) 모델을 분리해 성능·확장성·유지보수성을 극대화 | - 병목 해소 및 책임 분리 - CQRS 와 Event Sourcing 동시 적용 가능 | OrderWriteService , OrderReadService , Event Sourcing 연동 |
Domain Event | 도메인 내부의 중요한 상태 변화를 이벤트 객체로 표현하여 비동기, 모듈 간 느슨한 결합을 지원 | - 불변 이벤트 - 퍼블리시/구독 모델 - 추적성 보장 | OrderPlacedEvent , UserRegisteredEvent |
Essence Object | UI(form) ↔ 도메인 간 데이터 전송과 유효성 검증용, 가벼운 DTO 대용 객체 | - 도메인 순수성 유지 - 입력 검증과 바인딩 전용 | RegistrationEssence , ProfileUpdateEssence |
장점
항목 | 설명 |
---|---|
비즈니스 로직 명확화 | 도메인 개념과 규칙을 코드에서 명시적으로 표현하여 비즈니스 의도가 명확하게 전달됨 |
도메인 캡슐화 | Aggregate 내부에서만 상태 변경을 허용하여 외부와의 불필요한 결합 방지 |
일관성과 무결성 보장 | Aggregate Root 가 트랜잭션 및 규칙을 강제하여 데이터 일관성과 무결성을 유지 |
테스트 용이성 | 각 도메인 객체가 독립적이며 상태 기반으로 단위 테스트 작성이 수월함 |
유지보수성 향상 | Bounded Context 와 계층 구조를 통한 관심사 분리로 변경에 유연하게 대응 가능 |
확장성과 유연성 | 계층화 아키텍처 및 도메인 중심 설계를 통해 새로운 기능 추가 시 구조적 확장이 쉬움 |
도메인 지식의 보존 | 핵심 규칙과 로직이 코드 안에 녹아 있어, 문서 없이도 도메인 지식을 전달 가능 |
협업 효율성 개선 | 유비쿼터스 언어 (Ubiquitous Language) 를 기반으로 이해관계자 간 의사소통이 명확해짐 |
저장소 추상화로 인한 유연성 | Repository 를 통한 데이터 저장/조회 추상화로 DB 나 인프라 기술 변경이 쉬움 |
단점과 문제점 그리고 해결방안
단점
항목 | 설명 | 해결 방안 |
---|---|---|
초기 복잡성 | 패턴과 계층이 많아 간단한 애플리케이션에도 구조가 복잡해짐 | 핵심 도메인 범위부터 단계적으로 적용, KPI 기반 코드 리뷰 통한 확장 여부 결정 |
진입 장벽 | DDD, 패턴, 계층 이해에 시간이 소요됨 | 단계적 교육, 코드 템플릿/문서 제공 |
성능/추상화 비용 | 계층, 추상화, 이벤트 등의 도입으로 성능 오버헤드 발생 가능 | 성능 모니터링, 병목 부분 CQRS/캐싱/직접 접근 등 선택적 최적화 |
과도한 엔지니어링 | 단순 CRUD 에도 무분별한 설계 적용 시 비용 대비 실익 미흡 | 비즈니스 가치/복잡도 기반 판단, 적절한 경계선 내에서 패턴 사용 |
문제점 및 해결방안
모델 구현 부실
문제점 | 원인 | 영향 | 탐지 및 예방 방법 | 해결 방법 및 기법 |
---|---|---|---|---|
Anemic Domain Model | 로직을 Entity 에 넣지 않고 서비스 계층에만 몰아둠 | 도메인 객체가 빈 껍데기처럼, 의미 상실 | 코드 리뷰, 모델 테스팅 | 도메인 메서드 중심 설계, 엔티티로 로직 이동, Specification 사용 |
규칙 누락 | 비즈니스 규칙을 코드에 반영하지 않음 | 일관성 저해, 예외/버그 위험 | 테스트&코드 분석, 코드 리뷰 | Guard/Assertion 도입, Specification 정리 |
Aggregate 설계 오류
문제점 | 원인 | 영향 | 탐지 및 예방 방안 | 해결 방법 및 기법 |
---|---|---|---|---|
Aggregate 경계 불명확 | Transaction 경계 및 도메인 이해 부족 | 데이터 불일관성, 복잡도 증가 | 도메인 워크숍, 설계 검토 | Aggregate 재정의, CQRS/Event-driven 설계 |
Aggregate 지나치게 큼 | 루트가 너무 많은 책임과 멤버를 포함 | 성능 저하, 테스트 어려움 | 코드 프로파일링, 복잡도 분석 | 작은 Aggregate 로 분할, CQRS, 서비스로 일부 로직 분리 |
저장소 패턴 오용
문제점 | 원인 | 영향 | 탐지 및 예방 | 해결 방법 및 기법 |
---|---|---|---|---|
Repository 남용 | 모든 엔티티마다 저장소를 생성 | 코드베이스 복잡화 | Repository 수 검토, 코드 리뷰 | Aggregate Root 에만 저장소 제공, 불필요한 클래스 제거 |
ORM- 도메인 매핑 불일치 | ORM 이 도메인 경계와 자연스럽게 맞지 않음 | 모델 왜곡 및 단순화 | ORM 구조 분석, 스키마 리뷰 | Mapper 계층 도입, ORM 비즈니스 로직 분리 |
도전 과제
구분 | 도전 과제 | 원인 또는 배경 | 영향 또는 리스크 | 해결 방안 및 기법 |
---|---|---|---|---|
기술적 | Aggregate 적절 분리 | 트랜잭션 범위, 경계 설계 미흡 | 데이터 일관성 저하, 설계 오류 | 트랜잭션 중심 도메인 분석, CQRS, Aggregate 크기 가이드라인 |
이벤트 일관성 관리 | 도메인 이벤트/통합 이벤트 구분 어려움 | 외부 시스템과 비동기 동기화 문제 | 이벤트 설계 표준화, Event Sourcing, Outbox 패턴 | |
성능 및 트랜잭션 문제 | 과도한 객체 간 연결, 트랜잭션 충돌 | 응답 지연, Lock 경쟁 | CQRS, 캐싱, Async 처리, Bulk 처리 | |
테스트 코드 복잡화 | 계층/패턴 많아짐에 따른 단위 테스트 구성 어려움 | 테스트 커버리지 감소, 유지보수 저하 | 전용 테스트 DSL 도입, Test Fixture 구성, 통합 테스트 중심 구성 | |
시각화 및 자동화 도구 부족 | 설계 모델과 코드 싱크 자동화 도구 부족 | 경계 혼란, 설계 문서 누락 | 모델 시각화 도구 도입 (ex. Structurizr), Mermaid/PlantUML 기반 문서화 | |
레거시 통합 | 기존 시스템과 도메인 모델 충돌 | 통합 복잡도, 코드 분기 증가 | Anti-Corruption Layer, Adapter, 점진적 전환 | |
조직적 | 팀 역량 및 경험 부족 | DDD 와 Tactical 패턴 학습 경험 부족 | 적용 실패, 설계 오류 | 워크숍, 도메인 세션, 코드리뷰 기반 멘토링 |
도메인 전문가와 협업 부족 | 유비쿼터스 언어 미활용, 도메인 공유 미흡 | 요구사항 누락, 모델 부정확성 | Event Storming, 공유 문서, 정기 협업 세션 | |
경계 정의의 모호성 | 도메인 간 책임과 기능 분리 기준 불명확 | 책임 혼란, 모듈 간 결합도 증가 | Bounded Context 명세 정의, Context Map 활용 | |
전략적 | 대규모 시스템 적용 | 협업 인원 증가, 도메인 다층화 | 일관성 유지 어려움, 커뮤니케이션 비용 증가 | 문서 표준화, 계층적 도메인 전략, 공통 도메인 공유 체계 수립 |
실무 사용 예시
도메인 | 사용 목적 | 적용 패턴 / 기술 | 기대 효과 |
---|---|---|---|
이커머스 | 주문/결제 관리 | Order Aggregate , Payment Value Object | 트랜잭션 일관성, 가격 정책 및 결제 정보 캡슐화 |
금융 서비스 | 계좌 관리 | Account Entity , Money Value Object | 상태 추적, 환율 및 금액 로직 도메인화 |
모빌리티 플랫폼 | 예약/취소 처리 | Reservation Aggregate , Domain Event | CQRS + Event Sourcing 활용 가능, 비동기 확장성 확보 |
의료 시스템 | 환자 정보 기록 | Patient Aggregate , MedicalRecord Entity | 데이터 정합성 보장, 추적 가능한 변경 이력 관리 |
물류/배송 | 위치/상태 추적 | Shipment Aggregate , Location Value Object | 위치 정보 정합성 유지, 실시간 상태 추적 가능 |
활용 사례
사례 1: 이커머스 주문 시스템
시스템 구성:
- 주문 Aggregate (OrderRoot Entity + OrderItem VO + Address VO)
- Repository: OrderRepository (CRUD)
- Domain Service: 결제 확인 및 Inventory 확인
- Domain Event: 주문 완료 → Integration Event 발행
- Controller/API layer
활용 워크플로우:
- 주문 요청 → OrderService
- OrderRoot 생성 → 주문 VO/Entity 구성
- 결제 로직 Domain Service 호출
- OrderRoot.confirm() → 상태 transitioning
- Repository.save() → Inventory 업데이트 + Domain Event
- Domain Event → Integration Event 발행
차이 분석: 기존 트랜잭션 스크립트 방식과 비교 시, 도메인 중심 모델 설계로 로직 재사용성과 유지보수성 우위
sequenceDiagram participant API participant OrderService participant OrderAggregate participant PaymentService participant OrderRepository participant EventPublisher API->>OrderService: createOrder(command) OrderService->>OrderAggregate: Order.create(…) OrderAggregate->>PaymentService: verifyPayment() PaymentService-->>OrderAggregate: OK OrderAggregate->>OrderRepository: save(order) OrderRepository->>EventPublisher: publish(OrderPlacedEvent)
사례 2: 전자상거래 주문 관리 시스템
시스템 구성:
- Order Aggregate (주문 집합체)
- OrderItem Entity (주문 항목 엔티티)
- Money Value Object (금액 값 객체)
- Product Entity (상품 엔티티)
- Customer Entity (고객 엔티티)
시스템 구성 다이어그램:
graph LR subgraph "Order Aggregate" O[Order Root] OI[OrderItem] M[Money VO] end subgraph "Customer Aggregate" C[Customer Root] A[Address VO] end subgraph "Product Aggregate" P[Product Root] PR[Price VO] end O --> OI OI --> M O -.-> C OI -.-> P C --> A P --> PR
Workflow:
- 고객이 상품을 장바구니에 추가
- 주문 생성 시 Order Aggregate 생성
- 각 상품에 대해 OrderItem Entity 생성
- 총 금액 계산 및 Money Value Object 생성
- 결제 처리 및 주문 상태 업데이트
- 재고 차감 및 배송 프로세스 시작
역할:
- Order Aggregate: 주문의 일관성 보장, 비즈니스 규칙 적용
- OrderItem Entity: 개별 상품 주문 정보 관리
- Money Value Object: 금액 계산 및 통화 처리
기존 방식과의 차이점:
- 트랜잭션 스크립트 방식: 절차적 처리, 비즈니스 로직 분산
- 전술적 설계 방식: 객체 중심 처리, 비즈니스 로직 캡슐화
구현 예시
다음은 전자상거래 주문 시스템의 Python 구현 예시:
|
|
실무에서 효과적으로 적용하기 위한 고려사항 및 주의할 점
구분 | 고려사항 | 주의할 점 | 권장사항 |
---|---|---|---|
도메인 모델링 | 도메인 경계와 핵심 개념을 명확히 정의 | 기술 중심으로만 도메인 구성 지양 | 도메인 전문가와 협업, 유비쿼터스 언어 활용 |
패턴 적용 전략 | Aggregate , Entity , Value Object 등 핵심 패턴의 정확한 이해 | 모든 객체에 패턴 무분별 적용 | 교육 및 문서화 기반 선택적 적용 |
계층 구조 설계 | 계층별 책임 분리 및 역할 명확화 | 계층 간 의존도 모호하거나 순환 참조 발생 | 계층화 아키텍처 (Layered Architecture) 유지 |
Repository 설계 | 도메인 중심 인터페이스로 추상화 | DB 중심의 저장/조회 설계 | Aggregate Root 단위로 Repository 제공 |
Aggregate 설계 | 트랜잭션 경계, 일관성 규칙 고려 | 지나치게 큰 Aggregate 는 성능 저하 유발 | 작고 응집력 있는 단위로 구성 |
성능 최적화 | Command/Query 분리 | 복잡한 조회를 도메인 내부에서만 처리 | CQRS, Projection, Read Model 사용 |
테스트 전략 | 도메인 로직에 대한 단위 테스트 중심 | 통합 테스트만으로 신뢰성 확보 어려움 | 테스트 피라미드 적용 (Unit > Integration) |
최적화하기 위한 고려사항 및 주의할 점
항목 | 고려사항 또는 설명 | 주의할 점 | 권장사항 |
---|---|---|---|
Aggregate 크기 | Aggregate 는 트랜잭션 경계 단위로 작고 응집력 있게 유지 | 불필요한 관계까지 포함 시 메모리/쿼리 낭비 | 필요한 도메인만 포함, Lazy Loading 전략 적용 |
트랜잭션 관리 | Aggregate 단위로 트랜잭션을 제한 | 긴 트랜잭션 → deadlock 가능성 | CQRS 패턴 도입, 명확한 트랜잭션 경계 정의 |
이벤트 처리 | 비동기 이벤트 기반은 확장성과 분리도 장점 | 느린 소비자, 데이터 불일치 위험 | 이벤트 스키마 버저닝, 병렬 소비자 구조 설계 |
지연/즉시 로딩 전략 | 성능에 따라 fetch 전략을 선택적으로 설정 | N+1 쿼리 발생 | 적절한 FetchType 설정 (JPA), 로딩 시점 제어 |
ORM 연계 분리 | 도메인 모델과 ORM 분리를 통해 순수 도메인 유지 | ORM Entity 가 도메인에 침투 | 맵퍼 계층 도입, Clean Architecture 원칙 적용 |
테스트 전략 | 도메인 로직의 단위 테스트 중심 | 무조건 통합 테스트로 의존성 커짐 | 테스트 더블 (Mock), 병렬 테스트, 테스트 피라미드 적용 |
복잡성 관리 | 객체 수 증가로 인한 설계 복잡성 증가 | 도메인 오염, 패턴 과용 | 리팩토링 기준 수립, 도메인 설명 문서화 |
캐싱/메모리 최적화 | Value Object 는 불변성을 활용한 캐싱이 가능 | 생성 비용 증가 | 객체 풀링, 캐시 라이브러리 활용 |
CQRS ReadModel 설계 | Command/Query 분리 시 조회 모델 최적화 필요 | Projection 모델 없으면 유지보수 어려움 | Projection Test 가능 모델 정의, Index 설계 |
Guard vs Validator | 즉각 실패 (Guard) vs 유효성 체크 (Validator) 구분 | 모든 유효성 체크를 하나로 처리 시 가독성 저하 | 책임 분리, 구체적 예외 명시 |
모듈화 및 의존성 관리 | 도메인 간 의존성 최소화, 독립성 유지 | 과도한 결합 → 변경 파급 | 인터페이스 분리, Bounded Context 설계 |
변경 관리 대응성 | 요구사항 변경을 빠르게 반영 가능하도록 설계 | 도메인과 기능 간 강결합 | 도메인 이벤트 설계 기반의 유연한 흐름 구성 |
모니터링 및 추적성 | 도메인 이벤트 기반으로 비즈니스 상태를 추적 가능 | 기술 로그만 의존 시 도메인 가시성 저하 | Business Metric, Audit Log 구축 |
기타 사항
CQRS (Command Query Responsibility Segregation)
Tactical DDD 와 시너지 있음. Aggregate 의 쓰기 모델과 읽기 모델을 분리함으로써 성능과 확장성 개선 가능.Event Sourcing
Aggregate 의 상태 변화 이벤트를 저장해 도메인 이력을 추적하며, 보충적인 Audit, Rollback 구현 가능.도메인 모델 호라이즌 확장
점진적으로 새로운 Bounded Context 및 하위 Aggregate 를 도입하며 설계를 확장할 수 있음.디자인 자동화 도구
ArchUnit, Structurizr 등으로 모듈 및 계층 구조 규약 위반 자동 검증 가능.도메인 모델 시각화
C4 모델, UML 다이어그램 등을 통해 팀 내 공유 및 유지보수 지원.
주제와 관련하여 주목할 내용
카테고리 | 주제 | 항목 | 설명 |
---|---|---|---|
도메인 주도 설계 | 전술적 설계 구성 요소 | Aggregate | 도메인 모델의 일관성과 무결성 관리, 트랜잭션 경계 단위 역할 |
Entity | 고유 식별자 보유, 상태 변경 가능, 생명주기 관리 | ||
Value Object | 불변 객체, 속성 기반 동등성 판단 | ||
Repository vs Factory | 생성 책임과 저장 책임 분리로 역할 명확화 | ||
구현 기법 | Specification Pattern | 비즈니스 규칙 캡슐화 | 조건 검증 로직을 조합 가능한 객체로 외부화하여 재사용성과 테스트 용이성 확보 |
Essence Object | UI → Domain 데이터 전달 | DTO 대체 혹은 보완으로 사용자 입력 데이터 구조화 및 유효성 검증 | |
아키텍처 패턴 | Layered Architecture | 계층별 책임 분리 | 표현, 응용, 도메인, 인프라 계층으로 분리하여 유지보수성과 확장성 향상 |
헥사고날 아키텍처 | Ports and Adapters | 내부 도메인 로직과 외부 인터페이스를 명확히 분리하는 유연한 아키텍처 구조 | |
CQRS | Command/Query 분리 | 읽기와 쓰기 모델을 분리하여 성능 최적화와 구조 명확화 | |
이벤트 소싱 | Event Store | 상태 저장 대신 이벤트 저장 → 상태 재생성 가능 | |
모델링 기법 | Event Storming | 도메인 이벤트 기반 모델링 | 업무 흐름 중심의 시각적 도메인 설계 협업 워크숍 |
테스트 전략 | TDD (Test-Driven Development) | 테스트 우선 설계 | 요구사항을 코드로 빠르게 구체화, 도메인 규칙 검증 중심 테스트 유도 |
주제와 관련하여 반드시 학습해야할 내용
카테고리 | 주제 | 항목 | 설명 |
---|---|---|---|
객체지향 설계 원리 | OOP 원칙 | 캡슐화 (Encapsulation), 상속 (Inheritance), 다형성 (Polymorphism) | 객체 간 책임 분리와 재사용성을 위한 기본 설계 원칙 |
SOLID 원칙 | 단일 책임, 개방 - 폐쇄, 리스코프 치환, 인터페이스 분리, 의존 역전 | 유지보수성과 확장성을 높이기 위한 객체지향 설계의 5 대 핵심 원칙 | |
소프트웨어 설계 패턴 | GoF 디자인 패턴 | Factory, Strategy, Observer 등 23 가지 패턴 | 상황에 맞는 재사용 가능한 구조/행위/생성 패턴을 적용해 문제를 해결하는 설계법 |
소프트웨어 아키텍처 | 계층화 아키텍처 | 표현 (Presentation), 응용 (Application), 도메인 (Domain), 인프라 (Infra) | 시스템 구성요소의 관심사 분리를 통해 유지보수와 확장을 용이하게 하는 구조 |
도메인 주도 설계 | 전술적 설계 구성 요소 | Aggregate, Entity, Value Object | 도메인 복잡성 관리를 위한 핵심 구조화 방식 및 객체 정의 |
모델링 및 표현 도구 | UML | 클래스 다이어그램, 시퀀스 다이어그램 등 | 도메인 구조와 상호작용을 시각적으로 표현하여 명확한 설계 커뮤니케이션 지원 |
개발 프로세스 | 애자일 방법론 | 반복적 개발, 지속적 통합, 고객 협업 중심 | 도메인 전문가와 개발자의 지속 협업을 가능하게 하며, DDD 적용에 이상적 환경 제공 |
용어 정리
카테고리 | 용어 | 설명 |
---|---|---|
DDD 핵심 개념 | Entity | 고유 식별자를 가지며 상태 변화와 생명주기를 관리하는 도메인 객체 |
Value Object | 불변성을 가지며 속성의 조합으로 동일성을 판단하는 객체 | |
Aggregate | 연관된 도메인 객체 집합의 일관성을 보장하는 트랜잭션 단위 | |
Aggregate Root | Aggregate 내부의 변경 진입점으로, 외부에서는 루트를 통해서만 Aggregate 접근 허용 | |
Bounded Context | 하나의 도메인 모델이 명확하게 적용되는 경계, 모델 간 충돌을 방지함 | |
설계 패턴 | Domain Event | 도메인 내에서 발생한 중요한 상태 변화를 외부에 알리는 이벤트 객체 |
Specification Pattern | 복잡한 비즈니스 규칙을 객체로 추출해 캡슐화하고 재사용성을 높이는 패턴 | |
CQRS | Command(쓰기) 와 Query(읽기) 를 분리해 확장성과 성능을 향상하는 아키텍처 패턴 | |
Event Sourcing | 시스템 상태를 이벤트의 이력으로 저장하고 재구성하는 아키텍처 패턴 | |
보조 개념 | Guard | 도메인 객체 내부에서 즉시 유효성 검사를 수행해 불변식을 유지하는 방어 로직 |
Essence Object | UI 입력 데이터의 일시적 보관과 검증을 담당하는 경량 전달 객체 | |
설계 원칙 | Ubiquitous Language | 개발자와 도메인 전문가가 공유하는 공통 언어로, 코드와 문서에 동일하게 사용됨 |
SRP (단일 책임 원칙) | 클래스는 하나의 변경 이유만 가져야 하며 하나의 책임만 가져야 한다는 객체지향 원칙 | |
Clean Architecture | 도메인 중심 아키텍처로, 비즈니스 로직과 인프라스트럭처를 계층 분리하여 의존성 방향을 고정함 | |
아키텍처 도구 | Layered Architecture | 표현, 응용, 도메인, 인프라 계층으로 분리해 관심사와 책임을 명확히 하는 아키텍처 |
ArchUnit | Java 코드의 아키텍처 규칙을 테스트 기반으로 자동 검증하는 도구 | |
Structurizr | C4 모델을 기반으로 시스템 아키텍처를 시각화하는 도구 |
참고 및 출처
- Domain-Driven Design—Tactical Design | by Masoud Chelongar - Medium
- Tactical Domain Driven Design - Sumerge
- Activity/Technique: Tactic(al) Domain-Driven Design (DDD) - socadk.github.io
- The Principles Behind Domain Driven Design - Gleecus
- Using tactical DDD to design microservices - Learn Microsoft
- Anemic domain model - Wikipedia
- Domain-driven design - Wikipedia
- Understanding Domain-Driven Design: A Practical Approach - SensioLabs
- [DDD] Tactical Design Patterns Part 1: Domain Layer - dev.to
- Tactical Patterns in Domain-Driven Design (DDD) - Medium
- SOLID - Wikipedia
- Building a Python API Using CQRS: A Simple Guide - Medium