개인의 기록
  • 소개
  • 프로젝트
  • 글
  • 링크

© 2026 newgirok

← 글 목록

Domain Driven Design — 비즈니스 도메인을 중심으로 설계하는 방법

2025년 1월 5일
DDDDDDDomainBounded ContextAggregate

소프트웨어가 복잡해질수록 기술 중심의 설계는 비즈니스 요구와 멀어진다. Domain Driven Design(DDD) 은 코드와 비즈니스 언어를 일치시켜, 도메인 전문가와 개발자가 같은 모델을 공유하도록 만드는 설계 철학이다. 데이터베이스 스키마나 프레임워크 구조가 아니라, 비즈니스 문제 자체를 중심에 놓는다.

핵심 빌딩 블록

Entity는 고유한 식별자(ID)로 구분되는 객체다. 속성이 바뀌어도 같은 Entity로 취급된다.

Value Object는 식별자 없이 속성의 값으로만 동등성을 판단하는 객체다. 불변(immutable)이어야 하며, 도메인 개념을 명시적으로 표현하는 데 쓴다.

// Entity — ID로 동일성 판단
class Order {
  constructor(
    public readonly id: OrderId,
    public status: OrderStatus,
    public items: OrderItem[]
  ) {}
}

// Value Object — 값으로 동일성 판단, 불변
class Money {
  constructor(
    public readonly amount: number,
    public readonly currency: string
  ) {}

  equals(other: Money): boolean {
    return this.amount === other.amount && this.currency === other.currency;
  }
}

Value Object는 new Money(100, 'KRW')와 new Money(100, 'KRW')가 동등하다. Entity는 id가 다르면 내용이 같아도 다른 객체다.

Repository는 Aggregate를 저장하고 조회하는 인터페이스다. 영속성 기술(DB, ORM)은 구현체에 숨기고, 도메인 레이어는 인터페이스에만 의존한다.

Domain Service는 특정 Entity나 Value Object에 속하기 어려운 도메인 로직을 담는다. 상태를 갖지 않는다.

Aggregate

Aggregate는 연관된 Entity와 Value Object의 묶음으로, 하나의 Aggregate Root를 통해서만 외부에서 접근할 수 있다. 트랜잭션 경계와 일치한다.

[ Order (Aggregate Root) ]
  ├── OrderItem (Entity)
  │     └── Money (Value Object)
  └── ShippingAddress (Value Object)

외부에서 OrderItem을 직접 수정하지 않는다. 반드시 order.addItem(...) 처럼 Root를 통한다. 이로써 비즈니스 불변식(invariant)을 Root가 보장한다.

class Order {
  addItem(product: Product, quantity: number): void {
    if (this.status !== OrderStatus.DRAFT) {
      throw new Error('확정된 주문은 수정할 수 없습니다.');
    }
    this.items.push(new OrderItem(product.id, quantity, product.price));
  }
}

불변식(invariant): 어떤 상황에서도 항상 참이어야 하는 비즈니스 규칙. Aggregate Root가 이를 강제한다.

Bounded Context

대규모 시스템에서 하나의 도메인 모델로 전체를 표현하려 하면 모델이 오염된다. Bounded Context는 특정 도메인 모델이 유효한 명시적 경계다.

┌─────────────────────┐     ┌─────────────────────┐
│   Ordering Context  │     │  Inventory Context  │
│                     │     │                     │
│  Product            │────▶│  Product            │
│  (name, price, qty) │     │  (sku, stock, loc)  │
└─────────────────────┘     └─────────────────────┘

같은 "Product"라도 Ordering Context에서는 가격과 수량이 중요하고, Inventory Context에서는 재고 위치가 중요하다. 각 Context는 자신의 모델을 독립적으로 유지한다.

Context 간 통신은 Anti-Corruption Layer(ACL) 나 이벤트(Domain Event)로 경계를 보호한다.

계층 구조

레이어역할
PresentationHTTP, CLI 등 입력 처리
ApplicationUse Case 조율, 트랜잭션 관리
DomainEntity, Aggregate, Domain Service — 순수 비즈니스 로직
InfrastructureDB, 외부 API, Repository 구현체

Domain 레이어는 어떤 외부 프레임워크도 의존하지 않는다. 이 분리가 DDD의 핵심 가치다.

← 이전 글Event Driven Architecture — 이벤트 발생을 중심으로 서비스가 협력하는 방식
다음 글 →CQRS — 조회와 명령을 분리하는 아키텍처 패턴