도메인 주도 설계로 서비스 분리하기
학습 목표
- DDD의 핵심 개념 이해
- Bounded Context를 이용한 서비스 경계 설정
- Aggregate를 통한 데이터 일관성 관리
- 실전 이커머스 예제로 서비스 분리 실습
도메인 주도 설계(DDD)란?
Eric Evans가 제안한 소프트웨어 설계 방법론으로, 비즈니스 도메인을 중심으로 시스템을 설계합니다.
핵심 개념
- Ubiquitous Language: 개발자와 도메인 전문가가 사용하는 공통 언어
- Bounded Context: 특정 모델이 적용되는 경계
- Aggregate: 데이터 일관성을 유지하는 단위
- Domain Event: 도메인에서 발생하는 중요한 이벤트
Bounded Context로 서비스 분리
예제: 이커머스 시스템
잘못된 분리:
❌ User Service
❌ Product Service
❌ Order Service
문제점: 기술 중심 분리로 비즈니스 로직이 여러 서비스에 분산
올바른 분리 (Bounded Context 기반):
✅ Catalog Context (카탈로그)
- 상품 검색
- 상품 정보 관리
✅ Shopping Context (쇼핑)
- 장바구니
- 주문 생성
✅ Fulfillment Context (주문 처리)
- 결제 처리
- 배송 관리
✅ Customer Context (고객)
- 회원 정보
- 고객 등급
Bounded Context 식별 방법
1. 이벤트 스토밍
팀 전체가 모여 비즈니스 이벤트를 포스트잇에 적습니다:
[고객이 상품을 검색함] → [상품을 장바구니에 추가함]
→ [주문을 생성함] → [결제가 완료됨] → [상품이 배송됨]
2. 이벤트 그룹핑
관련된 이벤트들을 묶어 Bounded Context를 식별:
Catalog Context:
- 상품 검색됨
- 상품 정보 조회됨
Shopping Context:
- 장바구니에 추가됨
- 주문 생성됨
Fulfillment Context:
- 결제 완료됨
- 상품 배송됨
3. 컨텍스트 맵 그리기
graph TD
Customer["Customer Context<br/>회원 정보, 고객 등급"]
Catalog["Catalog Context<br/>상품 검색, 상품 정보"]
Shopping["Shopping Context<br/>장바구니, 주문 생성"]
Fulfillment["Fulfillment Context<br/>결제, 배송"]
Customer --> Catalog
Customer --> Shopping
Shopping --> Fulfillment
Catalog -.상품 정보 조회.-> Shopping
style Customer fill:#e1f5fe,stroke:#01579b,stroke-width:2px
style Catalog fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
style Shopping fill:#e8f5e9,stroke:#1b5e20,stroke-width:2px
style Fulfillment fill:#fff3e0,stroke:#e65100,stroke-width:2px
Aggregate 설계
Aggregate란?
관련된 객체들의 묶음으로, 일관성 경계를 정의합니다.
예제: 주문 Aggregate
// 주문 Aggregate Root
class Order {
private id: OrderId;
private customerId: CustomerId;
private items: OrderItem[];
private status: OrderStatus;
private totalAmount: Money;
addItem(product: Product, quantity: number) {
// 비즈니스 규칙: 재고 확인
if (product.stock < quantity) {
throw new InsufficientStockError();
}
const item = new OrderItem(product, quantity);
this.items.push(item);
this.recalculateTotal();
}
complete() {
// 비즈니스 규칙: 최소 금액 확인
if (this.totalAmount.isLessThan(MIN_ORDER_AMOUNT)) {
throw new MinimumAmountError();
}
this.status = OrderStatus.COMPLETED;
// Domain Event 발행
this.addDomainEvent(new OrderCompleted(this.id));
}
}
Aggregate 설계 원칙
- 작게 유지: 진짜 일관성이 필요한 것만 포함
- ID로 참조: 다른 Aggregate는 ID로만 참조
- 트랜잭션 경계: 하나의 Aggregate = 하나의 트랜잭션
Context 간 통신
1. REST API (동기)
// Shopping Context → Catalog Context
const product = await catalogService.getProduct(productId);
2. 메시지/이벤트 (비동기)
// Shopping Context가 이벤트 발행
eventBus.publish('order.created', {
orderId: '123',
items: [...],
});
// Fulfillment Context가 구독
eventBus.subscribe('order.created', async (event) => {
await processPayment(event.orderId);
});
실습: 이커머스 서비스 분리
요구사항
온라인 서점 시스템을 마이크로서비스로 분리하세요:
기능:
- 도서 검색 및 조회
- 장바구니 관리
- 주문 및 결제
- 배송 추적
- 회원 관리
해답 예시
1. Catalog Service
- 도서 검색
- 도서 정보 관리
- 재고 조회
2. Shopping Service
- 장바구니 CRUD
- 주문 생성
- 주문 내역 조회
3. Payment Service
- 결제 처리
- 결제 내역
4. Delivery Service
- 배송 상태 관리
- 배송 추적
5. Customer Service
- 회원 가입/로그인
- 프로필 관리
다음 강의
다음 강의에서는 API Gateway와 서비스 간 통신 패턴을 배웁니다.
핵심 정리
- DDD는 비즈니스 도메인 중심 설계 방법론
- Bounded Context로 서비스 경계를 명확히 설정
- Aggregate는 데이터 일관성의 단위
- 이벤트 스토밍으로 도메인 이해도 향상
- 서비스는 ID로 참조하고 이벤트로 통신