contents

도메인 주도 설계(Domain-Driven Design, DDD) 는 소프트웨어를 특정 비즈니스 도메인에 맞게 모델링하는 데 초점을 맞추는 소프트웨어 개발 접근법입니다. 이는 기술이나 프레임워크가 아니라, 비즈니스 로직이 주요 과제인 복잡한 시스템을 구축하기 위한 철학이자 패턴들의 집합입니다. 궁극적인 목표는 비즈니스가 실제로 동작하는 방식을 그대로 반영하는 소프트웨어를 만드는 것입니다.


DDD가 해결하는 문제

많은 전통적인 소프트웨어 프로젝트에서는 데이터베이스 스키마, 프레임워크, UI와 같은 기술에 주된 초점을 맞춥니다. 비즈니스 로직은 종종 서비스 클래스, UI 이벤트 핸들러, 데이터베이스 트리거 등 코드베이스 전반에 흩어지게 됩니다. 이는 "빈약한 도메인 모델(Anemic Domain Model)"로 알려진 상태로 이어지는데, 여기서 "도메인 객체"는 단지 데이터를 담는 컨테이너(getter와 setter만 가진)에 불과하고 실제 비즈니스 규칙은 모두 다른 곳에 존재하게 됩니다.

이는 다음과 같은 여러 문제를 야기합니다.

DDD는 도메인과 그 로직을 프로젝트의 절대적인 중심에 둠으로써 이 문제를 해결합니다.


DDD의 두 기둥: 전략적 설계와 전술적 설계

DDD는 두 가지 주요 패턴 범주를 통해 가장 잘 이해할 수 있습니다.

  1. 전략적 설계 (Strategic Design - 큰 그림 🗺️): 시스템의 상위 수준 아키텍처를 정의하는 것입니다. 크고 복잡한 도메인을 더 작고 관리하기 쉬운 조각으로 나누는 데 도움을 줍니다.

  2. 전술적 설계 (Tactical Design - 빌딩 블록 🧱): 시스템의 잘 정의된 한 부분 내에서 객체와 그 상호작용을 설계하는 것입니다.

각각을 자세히 살펴보겠습니다.


전략적 설계: 도메인 매핑

전략적 설계는 비즈니스 도메인 자체의 모델을 만드는 것에 관한 것입니다.

1. 유비쿼터스 언어 (Ubiquitous Language)

이는 DDD에서 가장 근본적인 개념입니다. 유비쿼터스 언어는 개발자, 도메인 전문가, 그리고 다른 이해관계자들이 협력하여 개발한 공통된 언어입니다.

2. 경계가 설정된 컨텍스트 (Bounded Context)

거대한 기업 전체를 위한 단일 통합 모델을 만드는 것은 비현실적이며 종종 혼란을 야기합니다. "Product(제품)"라는 단어는 다른 부서에서 다른 의미를 가질 수 있습니다.

경계가 설정된 컨텍스트는 특정 도메인 모델이 적용되고 유비쿼터스 언어가 명확하고 모호하지 않은 의미를 갖는 명시적인 경계입니다. 모든 부서를 위한 거대한 Product 클래스 하나를 만들려고 시도하는 대신, "영업 컨텍스트"에 맞는 Product 모델과 "배송 컨텍스트"에 맞는 다른 Product 모델을 각각 만듭니다.

3. 컨텍스트 맵 (Context Map)

컨텍스트 맵은 서로 다른 경계가 설정된 컨텍스트들 간의 관계를 보여주는 다이어그램입니다. 이는 시스템의 전체적인 아키텍처 뷰입니다. 일반적인 관계 패턴은 다음과 같습니다.


전술적 설계: 모델 구축

전술적 설계는 단일 경계 컨텍스트 내에서 풍부한 도메인 모델을 만들기 위한 빌딩 블록을 제공합니다.

1. 엔티티 (Entities)

엔티티는 속성이 아닌, 시간이 지나도 지속되는 고유한 식별자(identity) 로 정의되는 객체입니다. 두 엔티티는 속성이 같더라도 식별자가 다르기 때문에 다른 객체입니다.

2. 값 객체 (Value Objects)

값 객체는 식별자가 아닌 속성으로 정의되는 객체입니다. 개념적인 식별자가 없습니다. 두 값 객체는 모든 속성이 같으면 동일한 것으로 간주됩니다. 일반적으로 불변(immutable) 입니다.

3. 애그리게잇 (Aggregates)

애그리게잇은 데이터 변경 시 단일 단위로 취급되는 연관된 엔티티와 값 객체들의 묶음입니다. 이는 일관성을 유지하기 위한 핵심 패턴입니다.

예시: Order(주문)는 애그리게잇 루트입니다. OrderItem(주문 항목) 엔티티 목록과 ShippingAddress(배송 주소) 값 객체를 포함할 수 있습니다. 외부에서 OrderItem을 직접 변경할 수 없으며, 반드시 Order 객체를 통해야 합니다. 예를 들어, order.changeItemQuantity(itemId, newQuantity)와 같이 호출해야 합니다. 이를 통해 Order는 "주문의 총비용은 고객의 신용 한도를 초과할 수 없다"와 같은 불변식을 강제할 수 있습니다.

4. 리포지토리 (Repositories)

리포지토리는 애그리게잇 루트에 접근하고 영속성을 관리하기 위한 컬렉션과 유사한 인터페이스를 제공하는 객체입니다. 이는 도메인 모델을 SQL 데이터베이스나 NoSQL 저장소와 같은 하위 영속성 기술로부터 분리합니다.

5. 팩토리와 서비스 (Factories and Services)


DDD의 장점과 사용 시기 ✅

장점:

언제 DDD를 사용해야 하는가:

언제 DDD를 사용하면 안 되는가:

references