Youtbe Spring I/O에서 DDD 관련 영상을 보았지만 영어로 말해서 그런지 괜히 더 어렵게 느껴졌다. Youtube에는 DDD 관련 영상은 많지만 그래도 Spring을 다루는 채널의 영상을 보았다.
- DDD (Domain-Driven Design)는 에릭 에반스의 저서 “Domain-Driven Design: Tackling Complexity in the Heart of Software”에서 처음 소개된 이래 20년이 지난 지금(2025년)도 복잡한 소프트웨어 시스템을 설계하고 개발하는 데 있어 중요한 접근 방식으로 자리매김하고 있다. DDD는 단순히 특정 아키텍처를 강제하기 보다는 도메인의 프로세스와 규칙에 대한 풍부한 이해를 담은 도메인 모델을 프로그래밍하느 것을 중심에 두는 소프트웨어 개발 방법론이자 사고 방식이다
- 소프트웨어 개발은 비즈니스 압박, 기술 부채, 인프라 관리 등 다양한 도전 과제에 직면해 있으며, 종종 구현 세부사항에 매몰되어 정작 중요한 소프트웨어 설계에 충분한 시간을 할애하지 못하는 경우가 많다. DDD는 이러한 문제의 핵심을 파고들어 기술보다는 비즈니스 도메인 지식에 집중하여 더욱 견고하고 유지보수하기 쉬운 시스템을 구축하도록 이끌어준다
DDD의 핵심 원칙 및 실천 방법
도메인 이해 (Domain Understanding)
- 핵심: 복잡한 소프트웨어를 성공적으로 만들려면 해당 비즈니스 도메인에 대한 깊은 이해가 필수적이다
- 실천: 개발자가 직접 도메인 전문가(은행원, 기획자, PO 등)와 적극적으로 대화하고 협업하는 것이 가장 효과적이다. 이러한 대화를 통해 비즈니스 모델과 목표를 공유하고 정렬한다
복잡성 분할 (Problem Decomposition)
- 핵심: 도메인의 복잡성을 한 번에 다루려 하지 않고 더 작은 독립적인 문제들로 나누어 해결한 뒤 연결하는 방식이다
- 실천: 서브도메인을 도출하고 각각을 개별적으로 탐색하여 모델링한다
유비쿼터스 언어 (Ubiquitous Language)
- 핵심: 도메인 전문가와 개발자 등 모든 이해관계자가 동의하여 사용하는 “어디에나 존재하는” 공통 언어를 구축하는 것이다
- 실천: 사용자 스토리, 클래스명, 메서드명, 코드, 문서, 다이어그램 등 소프트웨어 개발의 모든 단계와 산출물에 동일한 도메인 용어를 일관되게 반영한다. 목표는 도메인 전문가가 코드를 읽고도 그 의미를 이해할 수 있을 만큼 코드와 비즈니스 언어 간의 간극을 줄이는 것이다
순수한 도메인 모델 개발
- 핵심: 도메인 모델은 프레임워크, 데이터베이스 등 구현 세부사항으로부터 독립적이고 순수해야 한다
- 실천: 학습한 도메인 지식을 반영하여 핵심 비즈니스 로직과 규칙을 담는 코드를 작성한다. 이때 엔티티(Entity), 값 객체(Value Object), 애그리게이트(Aggregate), 리포지토리(Repository), 팩토리(Factory) 등의 전술적 패턴을 활용한다
- 빈약한(Anemic) 도메인 모델 vs 풍부한(Rich) 도메인 모델
- 빈약한 모델(Bad Practice): 엔티티가 단순한 컨테이너 역할을 하고 모든 비즈니스 로직이 별도의 서비스 클래스에 집중되는 경우이다. 이는 서비스 클래스가 비대해지고 유지보수가 어려워지는 결과를 초래한다
- 풍부한 모델(Good Practice): 엔티티 자체가 자신의 상태 변화, 제약 조건, 비즈니스 규칙을 포함하며, 서비스 클래스는 이러한 도메인 객체들을 조율하는 유스케이스를 담당하는 구조이다
DDD의 모듈화 개념
Bounded Context (경계 컨텍스트)
- 정의: 특정 목적을 위해 일관된 유비쿼터스 언어로 표현되는 도메일 모델의 명확한 경계이다. 이는 시스템을 나누는 가장 큰 단위의 모듈이며, 도메인 경계를 의미한다
- 주의: 경계 컨텍스트는 도메인 모델의 논리적 경계이며, 마이크로서비스와 같은 ‘배포 단위의 경계’와는 다름을 명확히 구분해야 한다
- 구성: 각 경계 컨텍스트는 자체적인 도메인 모델, 규칙, 정책에 집중하며, 데이터 모델보다는 행동 중심 모델링(verbs first)을 강조한다. 예를 들어 ‘식물학’ 맥락의 토마토와 ‘요리’맥락의 토마토가 다른 의미를 가지듯이 컨텍스트에 따라 같은 용어도 다른 의미를 가질 수 있다
Aggregate (애그리게이트)
- 정의: 경계 컨텍스트 내부의 더 작은 모듈 단위로 Entity와 Value Object들을 그룹핑하여 하나의 일관된 단위로 관리한다
- 루트 엔티티(Root Entity): 애그리게이트의 유일한 진입점이자 참조점이며, 애그리게이트 전체의 생명주기를 관리하고 비즈니스 기능을 퍼사드(Facade)처럼 제공한다
- 설계 원칙: 애그리게이트 간에는 절대 직접 객체를 참조하지 않고 ID로만 참조하여 독립적인 변경 가능성을 확보하고 느슨한 결합을 유지한다. 비즈니스 규칙(invariants)을 일관되게 실행하는 단위가 된다
아키텍처와 DDD
DDD 자체는 특정 아키텍처를 강제하지 않지만, 헥사고날 아키텍처(Hexagonal Architecture), 오니언 아키텍처(Onion Architecture), 클린 아키텍처(Clean Architecture)와 함께 사용될 때 시너지를 낸다
- 공통점: 해당 아키텍처들은 모두 도메인 모델이 시스템의 중심에 위치하며, 모든 의존성의 방향이 항상 도메인 모델 쪽으로 향하는 것을 강조한다. 인프라(저장소, 메시징, UI)는 도메인에 종속된다
- 계층 구조
- 도메인 모델: 가장 안쪽에 위치하며, 다른 것에 대한 의존성이 없다
- 애플리케이션 서비스: 애플리케이션의 진입점으로 도메인 모델을 사용하여 유스케이스를 조율한다
- 사용자 인터페이스 (UI): Web API, Kafka 리스너 등으로, 애플리케이션 서비스와 상호작용한다
- 인프라스트럭처: 영속성, 데이터베이스, 캐싱, 외부 API 호출 등을 담당하며, 애플리케이션 서비스나 도메인 모델이 의존한다 (의존성 역전 원칙 DIP)
DDD는 단순히 코딩 기법이나 아키텍처 스타일을 넘어 복잡한 소프트웨어를 체계적으로 설계하고 유지보수하기 위한 사고 방식의 전환을 요구한다. 도메인 모델을 중심에 두고 보편적 언어를 통해 모든 이해관계자와 코드가 일관성을 갖도록 하며, 인프라와 아키텍처는 이 핵심 도메인 모델을 지원하는 역할만 맡게 만다는 철학이다
이는 프레임워크와 기술에 대한 고민보다 비즈니스 로직과 도메인 모델 개발에 노력을 집중하고 명확한 경계를 설정하며, 이벤트 기반 통신을 통해 모듈 간 느슨한 결합을 추구함으로써 변화하는 비즈니스 요구사항에 민첩하게 대응할 수 있는 견고하고 유연한 소프트웨어를 구축하는 데 기여한다. DDD는 첫 시도에 완벽할 수 없지만 지속적인 학습과 반복적인 설계를 통해 점진적으로 개선되는 과정이며, 복잡한 시스템의 장기적인 성공을 위한 필수적인 접근 방식이다
출처 – Getting modules right with Domain-driven Design by Michael Plöd @ Spring I/O 2022
출처 – Implementing Domain Driven Design with Spring by Maciej Walkowiak @ Spring I/O 2024