몇 년 전, 한 스타트업의 개발팀 리드가 이런 고민을 털어놓은 적이 있어요. “서비스가 커질수록 코드가 점점 뒤엉키는데, 어디서부터 손을 대야 할지 모르겠다”고요. 처음엔 단순했던 주문 처리 로직이 어느새 결제, 배송, 쿠폰, 정산 모듈과 거미줄처럼 엮여버린 거죠. 이 문제, 개발 경험이 조금이라도 있으신 분이라면 고개를 끄덕이실 것 같아요. 바로 이 지점에서 도메인 주도 설계(Domain-Driven Design, DDD)가 빛을 발합니다. 2026년 현재, DDD는 마이크로서비스 아키텍처와 함께 엔터프라이즈 개발의 핵심 방법론으로 자리 잡았고, 단순한 설계 이론을 넘어 실무에서 반드시 익혀야 할 역량이 됐다고 봅니다.

📊 왜 지금 DDD인가 — 수치로 보는 복잡성의 함정
먼저 규모의 문제를 짚고 넘어갈게요. 2026년 기준 글로벌 소프트웨어 개발 생산성 연구(State of DevOps 2025 후속 리포트 기준)에 따르면, 500명 이상 규모 조직의 개발팀 중 약 68%가 “기능 추가보다 기존 코드 이해에 더 많은 시간을 쓴다”고 응답했어요. 심지어 신규 기능 개발 시간의 평균 43%가 레거시 코드 파악과 사이드 이펙트 검토에 소비된다는 결과도 있죠. 이건 단순한 기술 부채(Technical Debt)의 문제가 아니라, 비즈니스 개념과 코드 사이의 언어 단절에서 오는 구조적 문제라고 봅니다.
DDD가 제시하는 해법의 핵심은 유비쿼터스 언어(Ubiquitous Language)예요. 개발자와 기획자, 도메인 전문가가 같은 단어로 같은 개념을 이야기하게 만드는 것이죠. “주문”이 개발 DB에선 order_tb, 기획서에선 “구매 요청”, 고객센터에선 “접수 건”으로 제각각 불리는 혼란을 없애는 거예요.
🧩 DDD의 핵심 개념, 현실 언어로 풀어보기
DDD를 처음 접하면 용어 때문에 압도되는 경우가 많아요. 전략적 설계와 전술적 설계로 나눠서 차근차근 살펴볼게요.
전략적 설계 (Strategic Design)는 큰 그림을 그리는 단계예요. 서비스 전체를 어떻게 나누고 각 영역이 어떻게 소통할지를 정의합니다.
- 바운디드 컨텍스트 (Bounded Context): 도메인 모델이 유효한 경계선이에요. 예를 들어 “고객”이라는 개념이 마케팅 팀에선 잠재 리드(Lead)지만, 주문 팀에선 실구매자(Customer)예요. 같은 단어라도 컨텍스트가 다르면 다른 모델이 필요합니다.
- 컨텍스트 맵 (Context Map): 여러 바운디드 컨텍스트가 어떻게 관계 맺는지 시각화한 지도예요. Upstream/Downstream 관계, Shared Kernel, Anti-Corruption Layer 같은 패턴으로 통합 방식을 정의합니다.
- 도메인 (Domain) / 서브도메인 (Subdomain): 비즈니스 문제 영역 자체를 뜻해요. 핵심 도메인(Core Domain)에 가장 많은 투자를 집중하고, 일반 도메인(Generic Domain)은 외부 솔루션으로 대체하는 판단이 중요합니다.
전술적 설계 (Tactical Design)는 실제 코드 수준에서 도메인을 표현하는 방법이에요.
- 엔티티 (Entity): 고유 식별자(ID)로 구분되는 객체. 상태가 변해도 동일한 존재로 취급해요. (예: 주문 ID가 같으면 같은 주문)
- 값 객체 (Value Object): 식별자 없이 속성 값 자체가 동일성을 나타내요. 불변(Immutable)으로 설계하는 게 원칙이에요. (예: 배송지 주소)
- 애그리게이트 (Aggregate): 관련 엔티티와 값 객체의 클러스터. 외부에서는 반드시 애그리게이트 루트(Aggregate Root)를 통해서만 접근해야 해요. 이게 데이터 일관성을 지키는 핵심 장치입니다.
- 도메인 이벤트 (Domain Event): 도메인 내에서 발생한 중요한 사실을 표현해요. “주문이 생성되었다(OrderCreated)”처럼 과거형으로 명명하고, 이벤트 기반 아키텍처와 자연스럽게 연결됩니다.
- 레포지토리 (Repository): 애그리게이트를 영속화하고 조회하는 인터페이스. 인프라 계층(DB)과 도메인 계층을 분리해주는 역할이에요.
- 도메인 서비스 (Domain Service): 특정 엔티티에 속하기 어색한 도메인 로직을 담는 곳이에요. 단, 무분별하게 남용하면 빈혈 도메인 모델(Anemic Domain Model)이 되기 쉬우니 주의가 필요합니다.

🌍 국내외 실무 적용 사례 — 이론이 현실이 되는 순간
해외 사례 — Uber Eats의 컨텍스트 분리
Uber Eats는 주문(Ordering), 레스토랑 파트너(Restaurant), 배달(Delivery), 결제(Payment) 를 명확한 바운디드 컨텍스트로 분리한 것으로 알려져 있어요. 특히 “메뉴 아이템”이 레스토랑 컨텍스트에서는 관리 대상 상품이고, 주문 컨텍스트에서는 구매 확정 스냅샷이에요. 동일한 개념을 컨텍스트별로 다른 모델로 관리함으로써, 한쪽 메뉴가 바뀌어도 과거 주문 데이터가 오염되지 않는 구조를 만들었다고 봐도 인 것 같습니다.
국내 사례 — 카카오 커머스의 MSA 전환
카카오 커머스(현 카카오쇼핑) 계열의 개발 블로그에서 공개된 내용에 따르면, 모놀리식 커머스 시스템을 마이크로서비스로 전환하는 과정에서 DDD의 이벤트 스토밍(Event Storming) 기법을 적극 활용했어요. 이벤트 스토밍은 도메인 전문가와 개발자가 포스트잇을 붙여가며 비즈니스 이벤트의 흐름을 함께 시각화하는 워크숍 방식인데, 이를 통해 바운디드 컨텍스트 경계를 합의하고 서비스 분리 기준을 도출했다고 해요. 기술 주도가 아닌 비즈니스 흐름 주도로 설계가 시작됐다는 점이 인상적이에요.
⚠️ 실무에서 자주 빠지는 함정들
- 빈혈 도메인 모델(Anemic Domain Model): 엔티티가 getter/setter만 가득하고 로직은 전부 서비스 레이어에 몰려있는 구조예요. DDD 용어를 쓰지만 실제로는 트랜잭션 스크립트 패턴이 되어버립니다.
- 애그리게이트 경계 설정 실패: 너무 큰 애그리게이트는 성능 문제를 야기하고, 너무 작은 애그리게이트는 일관성 유지가 어려워요. “한 트랜잭션에 하나의 애그리게이트”를 원칙으로 삼고 시작하는 게 좋습니다.
- 레포지토리를 DAO처럼 쓰는 것: 레포지토리는 도메인 관점의 컬렉션처럼 다뤄야 해요.
findByStatusAndDateBetween()같은 인프라 냄새 나는 메서드보다findPendingOrdersOf(customerId)처럼 도메인 언어로 표현하는 게 원칙입니다. - 모든 서비스에 DDD를 적용하려는 욕심: CRUD 성격의 단순한 서브도메인에 DDD 전술 패턴을 모두 도입하면 오히려 복잡성만 늘어나요. 핵심 도메인에만 집중 투자하는 판단이 중요합니다.
🛠️ 2026년 실무에서 DDD를 시작하는 현실적인 방법
처음부터 “완벽한 DDD 아키텍처\
태그: []
Leave a Reply