도메인 주도 개발 (DDD, Domain Driven Development)
비즈니스 도메인을 중심으로 설계하고 구현하는 접근 방식
시스템을 기술적 관점이 아니라 비즈니스 문제 해결 관점에서 모델링하고 설계하는 방법론
주요 특징
도메인 전문가와 협업 (개발자와 도메인 전문가가 함께 모델을 정의)
도메인 모델 중심 (객체와 클래스는 단순 코드가 아니라 비즈니스 개념을 반영)
복잡한 도메인 해결에 최적화 (단순 CRUD 중심 시스템보다 복잡한 규칙, 프로세스를 가진 시스템에서 효과적)
핵심 개념
도메인 : 비즈니스 영역, 문제 영역
유비쿼터스 언어 : 개발자와 도메인 전문가가 공유하는 공통 언어
엔티티 : 식별 가능한 객체
값 객체 : 식별자 없이 속성으로 정의되는 객체
애그리거트 (Aggregate) : 도메인 객체의 그룹, 일관성 경계 설정
레포지터리 (Repository) : 도메인 객체의 저장과 조회를 담당
서비스 : 엔티티나 값 객체가 담당하기 어려운 도메인 로직 수행
팩토리 : 복잡한 객체 생성 담당
장점
복잡한 비즈니스 모델링에 강점 (실제 도메인을 코드에 반영해 이해하기 쉬움)
유지보수 용이 (도메인 모델이 명확하면 기능 추가/수정 시 영향 범위를 쉽게 파악 가능)
도메인 전문가와 소통 원활 (유비쿼터스 언어를 사용하면 의사소통 오류 최소화)
테스트 용이 (도메인 로직이 중심이므로 단위 테스트 중심 개발 가능)
단점
초기 학습 비용 높음 (엔티티, 값, 객체, 애그리거트 등 개념을 이해해야함)
단순 시스템에는 오버헤드 (작은 CRUD 중심 시스템에서는 과도한 설계가 될 수 있음)
설계 복잡성 증가 (올바른 경계 설정과 모델링에 시간 소요)
경험 없는 팀에서 구현 어려움 (도메인 전문가와 긴밀한 협업 필수)
워크 플로우
1. 도메인 이해
- 도메인 전문가와 인터뷰, 도메인 분석
- 핵심 개념, 용어, 프로세스 정의
- 유비쿼터스 언어 확립
[example]
도메인 : 주문(Order), 상품(Product), 결재(Payment)
유비쿼터스 언어 : 주문 생성, 결제 승인, 상품 재고 감소
2. 도메인 모델링
- 엔티티, 값, 객체, 애그리거트 정의
- 도메인 이벤트, 서비스, 규칙 도출
- 경계 (Bounded Context) 설정 (복잡한 시스템을 모듈 단위로 나눔)
[example]
# 엔티티
Order
- orderId : UUID
- customerId : UUID
- orderItems : List<OrderItem>
- status : OrderStatus
Product
- productId : UUID
- name : String
- price : Decimal
- stock : Int
# 값 객체
OrderItem
- productId : UUID
- quantity : Int
- price : Decimal
3. 설계
- 계층 설계 (Domain Layer, Application Layer, Infrastructure Layer, Interface Layer)
- 애그리거트 루트 정의 및 레포지터리 설계
- 도메인 로직 중심의 객체 관계 설계
[example]
# 애그리거트
Order Aggregate : Order(루트), OrderItem(값 객체)
Product Aggregate : Product(루트)
# 도메인 서비스
- 주문 생성 시 재고를 확인하고, 재고 감소 처리
OrderService
- createOrder(customerId, List<OrderItem>)
# 레포지터리
- 도메인 객체 저장과 조회 담당
OrderRepository
- save(Order order)
- findById(UUID orderId)
ProductRepository
- findById(UUID productId)
- update(Product product)
[example]
# 워크 플로우
1. 고객이 상품 선택 > 주문 생성 요청
2. OrderService에서 재고 확인
3. 재고 충분 > 주문 생성, 결제 대기 상태
4. 결제 완료 > 주문 상태 변경, 재고 감소
5. 주문 Aggregate 저장 (OrderRepository)
4. 구현
- 도메인 모델을 코드로 구현
- 레포지터리와 서비스 구현
- 단위 테스트 및 통합 테스트 수행
// 엔티티
class Order {
constructor(
public orderId: string,
public customerId: string,
public items: OrderItem[],
public status: 'Pending' | 'Paid'
) {}
pay() {
if (this.status !== 'Pending') throw new Error('Already paid');
this.status = 'Paid';
}
}
// 값 객체
class OrderItem {
constructor(
public productId: string,
public quantity: number,
public price: number
) {}
}
// 도메인 서비스
class OrderService {
constructor(
private orderRepo: OrderRepository,
private productRepo: ProductRepository
) {}
createOrder(customerId: string, items: OrderItem[]): Order {
items.forEach(item => {
const product = this.productRepo.findById(item.productId);
if (product.stock < item.quantity) throw new Error('Out of stock');
product.stock -= item.quantity;
this.productRepo.update(product);
});
const order = new Order(Date.now().toString(), customerId, items, 'Pending');
this.orderRepo.save(order);
return order;
}
}
5. 배포 및 반복
- 피드백 기반으로 도메인 모델 개선
- 새로운 요구사항 반영
'Development > Development' 카테고리의 다른 글
| 알고리즘 실행 시간 (시간 복잡도)에 대해 알아보자 (0) | 2025.10.19 |
|---|---|
| IKEA 설명서 스타일로 알고리즘 설명하기 (0) | 2025.10.19 |
| 개발을 효율화하는 최신 도구 8선 (0) | 2025.10.19 |
| 커서 (cursor)를 더 똑똑하게 사용하고 싶은 분들을 위한 팁 12개 (0) | 2025.10.19 |
| GoF 디자인 패턴 한 줄 요약 (2) | 2025.08.27 |
