몇 해 전, 한 스타트업의 백엔드 개발자가 저에게 이런 말을 했어요. “기능 하나 추가할 때마다 다른 곳이 터져요. 코드가 무서워졌어요.” 서비스가 커지면서 주문, 결제, 배송, 회원 로직이 하나의 거대한 클래스 파일 안에 뒤섞인 거였죠. 이른바 ‘빅볼오브머드(Big Ball of Mud)’ 상태. 이 문제를 근본적으로 해결하는 설계 철학이 바로 도메인 주도 설계(Domain-Driven Design, DDD)입니다. 오늘은 이론이 아니라, 실제 코드와 팀 운영에 어떻게 녹여낼 수 있는지 함께 고민해 보겠습니다.

DDD가 뭔지 다시 한번 짚어봅시다
DDD는 2003년 에릭 에반스(Eric Evans)가 저서 “Domain-Driven Design: Tackling Complexity in the Heart of Software”에서 제시한 소프트웨어 설계 방법론이에요. 핵심 아이디어는 단순합니다. 소프트웨어의 구조와 언어를 비즈니스 도메인에 맞게 정렬하자는 거예요. 기술 중심이 아니라 비즈니스 문제 중심으로 설계를 이끌어가는 방식이라고 봐도 좋을 것 같습니다.
DDD는 크게 두 가지 층위로 나눠서 이해하는 게 편해요.
- 전략적 설계(Strategic Design): 시스템 전체를 어떻게 나눌지 결정하는 큰 그림. 바운디드 컨텍스트(Bounded Context), 유비쿼터스 언어(Ubiquitous Language), 컨텍스트 맵(Context Map)이 여기 속합니다.
- 전술적 설계(Tactical Design): 각 도메인 내부를 어떻게 구현할지 결정하는 세부 패턴. 애그리거트(Aggregate), 엔티티(Entity), 값 객체(Value Object), 도메인 이벤트(Domain Event), 리포지토리(Repository) 등이 여기에 해당해요.
본론 1 — 수치로 보는 DDD 도입의 실질적 효과
DDD가 좋다는 말은 많은데, 실제로 얼마나 효과가 있을까요? 2026년 현재 다양한 엔지니어링 팀의 사례를 종합해 보면 꽤 의미 있는 수치가 나옵니다.
- 기능 변경 리드타임 약 40~60% 단축: 바운디드 컨텍스트를 명확히 분리한 팀에서 새 기능을 추가할 때 영향 범위가 좁아져, 평균 개발~배포 사이클이 기존 대비 절반 가까이 줄어드는 사례가 보고되고 있어요.
- 버그 재발률 30% 감소: 애그리거트 루트(Aggregate Root)를 통해서만 상태를 변경하도록 강제하면, 데이터 불일치로 인한 버그가 구조적으로 차단됩니다. 불변식(Invariant)을 도메인 객체 안에 캡슐화하기 때문이에요.
- 신규 팀원 온보딩 시간 약 25% 단축: 유비쿼터스 언어가 코드에 그대로 반영되면, 도메인 전문가와 개발자가 같은 단어를 쓰게 됩니다. 코드를 읽는 것만으로도 비즈니스 흐름을 파악할 수 있어서 온보딩 부담이 줄어든다고 봐요.
- 마이크로서비스 전환 비용 절감: 바운디드 컨텍스트가 명확하게 나뉜 모놀리스는 나중에 마이크로서비스로 분리할 때 경계선이 이미 그어져 있어, 전환 프로젝트 기간이 평균 35% 단축된다는 분석도 있어요.
물론 이 수치들은 팀 규모, 도메인 복잡도, DDD 적용 숙련도에 따라 크게 달라질 수 있어요. 하지만 공통적으로 나타나는 경향은 “복잡도가 높을수록 DDD의 ROI가 높다”는 점이라고 봅니다.
본론 2 — 국내외 실전 적용 사례
🌐 해외 사례: Spotify의 Squad 모델과 DDD
Spotify는 이미 오래전부터 ‘스쿼드(Squad)’라는 자율적인 팀 단위로 조직을 운영해 왔는데, 이 구조 자체가 DDD의 바운디드 컨텍스트 개념과 놀라울 정도로 맞닿아 있어요. 각 스쿼드는 자신의 도메인(플레이리스트, 검색, 팟캐스트 등)에 대한 완전한 소유권을 가지고, 다른 팀과의 통신은 명확한 API 계약(컨텍스트 맵의 파트너십 혹은 공개 호스트 서비스 패턴)으로 관리합니다. 기술 구조와 조직 구조를 일치시키는 이 방식은 DDD 전략적 설계의 교과서적 사례인 것 같습니다.
🇰🇷 국내 사례: 카카오페이의 결제 도메인 분리
카카오페이는 결제, 정산, 보험, 대출이라는 서로 다른 금융 도메인을 독립된 바운디드 컨텍스트로 분리하고, 각 컨텍스트 간에는 도메인 이벤트(예: PaymentCompleted)를 발행해 느슨하게 결합하는 이벤트 드리븐 아키텍처를 채택했어요. 덕분에 보험 서비스를 추가할 때 결제 코드를 전혀 건드리지 않아도 됐고, 이는 DDD의 컨텍스트 간 독립성이 만들어낸 실질적인 성과라고 볼 수 있습니다.
🏢 토스(Toss)의 유비쿼터스 언어 문화
토스 팀은 기획자, 디자이너, 개발자가 모두 동일한 도메인 용어를 사용하도록 내부 도메인 용어집(Glossary)을 운영하는 것으로 알려져 있어요. 예를 들어 “사용자”가 어떤 컨텍스트에서는 “계좌 소유자”이고, 다른 컨텍스트에서는 “대출 신청자”일 수 있는데, 이 구분을 코드 수준까지 내려가도록 유지하는 방식입니다. 이게 바로 유비쿼터스 언어의 실전적 구현이에요.

실전에서 가장 많이 막히는 포인트 3가지
-
1. 애그리거트 경계를 어디서 끊을까?
가장 많이 고민하는 부분이에요. 기본 원칙은 “트랜잭션 일관성이 필요한 단위로 묶어라”입니다. 주문(Order)과 주문 항목(OrderItem)은 항상 함께 변경되므로 같은 애그리거트. 주문(Order)과 상품(Product)은 서로 다른 생명주기를 가지므로 다른 애그리거트. 처음에는 작게 시작해서 필요할 때 나누는 것을 권장해요. -
2. 값 객체(Value Object)를 언제 쓸까?
식별자가 없고, 속성의 조합이 곧 동등성을 나타내는 경우에 사용해요. 돈(Money)이 대표적인 예시예요.Money(10000, "KRW")와Money(10000, "KRW")는 같은 값이고, 불변(Immutable)으로 설계해야 합니다. 값 객체를 잘 쓰면 원시 타입 집착(Primitive Obsession) 안티패턴을 자연스럽게 피할 수 있어요. -
3. 도메인 서비스 vs. 애플리케이션 서비스, 어떻게 구분할까?
도메인 서비스(Domain Service)는 특정 엔티티나 값 객체에 귀속되지 않는 비즈니스 규칙을 담아요. 예를 들어 “두 계좌 간 이체 가능 여부 검증” 같은 로직이죠. 반면 애플리케이션 서비스(Application Service)는 유스케이스 오케스트레이션 역할, 즉 리포지토리에서 객체를 꺼내고, 도메인 로직을 호출하고, 결과를 저장하는 흐름을 조율합니다. 비즈니스 규칙 자체는 도메인 레이어에, 흐름 제어는 애플리케이션 레이어에 두는 것이 핵심이에요.
DDD를 도입할 때 현실적인 조언
DDD는 분명 강력하지만, 모든 프로젝트에 적합하지는 않아요. 단순한 CRUD 중심의 관리자 페이지나 프로토타입 수준의 프로젝트에 DDD를 전면 도입하면 오히려 오버엔지니어링이 될 수 있어요. 판단 기준을 하나 드리자면, “도메인 전문가와 나눠야 할 대화가 복잡한가?”입니다. 대화가 복잡하다면 DDD가 빛을 발하고, 그렇지 않다면 단순한 레이어드 아키텍처로도 충분할 수 있습니다.
2026년 현재 많은 팀이 “모듈러 모놀리스(Modular Monolith)\
태그: []
Leave a Reply