| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | |||||
| 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 10 | 11 | 12 | 13 | 14 | 15 | 16 |
| 17 | 18 | 19 | 20 | 21 | 22 | 23 |
| 24 | 25 | 26 | 27 | 28 | 29 | 30 |
| 31 |
- builderPattern
- PrototypePattern
- compose
- material3
- 옵저버 패턴
- 코루틴
- android designsystem
- Observer Pattern
- ㅋㅁ
- 함수형프로그래밍
- 디자인패턴
- 안드로이드 디자인시스템
- 추상팩토리패턴
- designPattern
- Functional Programming
- define
- Kotlin
- 추상 팩토리
- 코틀린
- Abstract Factory
- 프로토타입 패턴
- Design Pattern
- Coroutines
- kmp
- 코틀린멀티플랫폼
- kotlin multiplatform
- 빌터패턴
- factory method
- 디자인패턴 #
- 팩토리 메소드
- Today
- Total
오늘도 더 나은 코드를 작성하였습니까?
도메인 주도 설계 (Domain Driven Design - DDD)의 기초 개념정리1. 본문
정의
도메인 주도 설계(DDD)는 소프트웨어를 그것이 다루는 업무 분야(도메인)에 맞춰 모델링하는 설계 방식입니다. 특정 기술이나 원칙이 아니라 사고하는 방법에 가까우며, 복잡한 업무 분야를 다루는 프로젝트에서 핵심을 놓치지 않도록 돕는 접근입니다. 핵심은 도메인 전문가와 개발자가 같은 용어(유비쿼터스 언어)로 소통해 이해의 차이에서 오는 오해를 없애고, 비즈니스 문제를 제대로 풀어내는 소프트웨어를 만드는 것입니다. 코드가 곧 비즈니스 지도가 되게 함으로써, 요구사항 변화에 유연하게 대응하고 소프트웨어의 유지보수성을 극대화하는 설계 철학입니다.

전략적 설계 (Strategic Design)

Problem Space (문제 공간)
비즈니스 자체를 이해하는 영역입니다. "우리가 어떤 문제를 해결하려는 거지?"
1. 도메인(Domain)과 서브도메인(Subdomain)
도메인은 비즈니스 영역 전체를 뜻합니다. 쿠팡이라면 "이커머스" 전체가 도메인이다.. 하지만 도메인 전체를 한 번에 다루기엔 너무 크기 때문에 서브 도메인으로 쪼갠다. 쿠팡 도메인이라면 상품 카탈로그, 주문, 결제, 배송, 리뷰, 회원, 검색, 추천 등으로 나눌 수 있다.
서브 도메인의 분류
Core Subdomain (핵심 서브도메인)
- 비즈니스의 경쟁력이 나오는 영역. 회사가 살아남는 이유 그 자체.
- 가장 똑똑한 개발자, 가장 많은 리소스, 가장 정교한 모델링이 들어가야 합니다.
- 절대 외주 주거나 패키지 사서 쓰면 안 되는 영역.
- 예: 쿠팡의 "물류·배송 최적화", 토스의 "송금·인증", 넷플릭스의 "추천 알고리즘".
Supporting Subdomain (지원 서브도메인)
- 핵심을 받쳐주지만, 그 자체로 차별화 포인트는 아닌 영역.
- 우리 비즈니스에 맞게 자체 개발은 해야 하지만, Core만큼 공들일 필요는 없음.
- 단순한 CRUD에 가까울 수 있고, 주니어가 맡아도 되는 영역.
- 예: 쿠팡의 "리뷰 시스템", "쿠폰 관리".
Generic Subdomain (일반 서브도메인)
- 어느 회사나 똑같이 필요한 범용 영역.
- 직접 만들지 말고 외부 솔루션·SaaS·오픈소스로 해결하는 게 합리적.
- 직접 만들면 시간 낭비.
- 예: 인증/로그인 → Auth0·Firebase Auth, 결제 게이트웨이 → 토스페이먼츠, 이메일 발송 → SendGrid.
이 분류가 중요한 이유는 "어디에 자원을 집중할지" 정하는 의사결정 도구이기 때문입니다. Core에는 베스트 인력을, Generic에는 SaaS를. 안드로이드 앱으로 비유하면 비즈니스 핵심 화면은 직접 정성껏 만들고, 로그인은 Firebase Auth 붙이고, 푸시는 FCM 쓰는 것
2. Domain Experts (도메인 전문가)
해당 비즈니스를 가장 잘 아는 사람. 개발자가 아닌 현업 담당자를 의미합니다. 보험 도메인이면 보험 설계사, 물류면 창고 관리자, 의료면 의사. 이들의 머릿속에 있는 암묵지를 끌어내는 게 도메인 분석의 출발점.
3. Knowledge Discovery (지식 수집)
도메인 전문가의 머릿속에 있는 비즈니스 지식을 발굴하는 활동. 인터뷰, 워크샵, 도메인 문서 분석, 실제 업무 관찰 등으로 이루어진다. 개발자가 일방적으로 듣는 게 아니라, 질문하고 모델로 그려보고 다시 검증받는 협력 과정.
4. Event Storming (이벤트 스토밍)
Knowledge Discovery의 대표적 기법. 큰 벽에 포스트잇을 붙여가며 도메인을 시각화하는 워크샵.
이벤트스토밍 과정에서 자연스럽게 바운디드 컨텍스트의 경계가 드러납니다. 이벤트의 흐름이 끊기는 지점, 용어가 달라지는 지점이 컨텍스트 경계가 된다.
5. Communication & Ubiquitous Language (소통과 유비쿼터스 언어)
도메인 전문가와 개발자가 같은 단어로 대화하기로 합의한 언어. 번역하지 않는 것이 핵심입니다.
흔한 안티패턴:
- 현업: "고객이 장바구니에 담은 상품"
- 개발자: "User의 Cart 엔티티 안에 있는 ProductItem"
- 회의에서는 매번 머릿속으로 번역 → 오해와 버그의 원천
유비쿼터스 언어를 쓰면:
- 회의에서도, 코드에서도, 문서에서도 모두 똑같이 "장바구니"와 "상품"을 사용
- 클래스명 Cart, 메서드명 addProduct() — 코드가 곧 도메인 언어
- Kotlin으로 보면: class Cart { fun addProduct(product: Product) } 이런 식으로 비즈니스 용어가 그대로 코드에 드러남
*주의할 점은 유비쿼터스 언어가 컨텍스트별로 다르다는 것. 주문 컨텍스트의 "상품"과 배송 컨텍스트의 "상품"은 다른 모델일 수 있습니다. 그래서 다음에 나오는 바운디드 컨텍스트가 필요해집니다.
Solution Space (해결 공간)
문제를 어떻게 시스템 구조로 풀어낼지를 다루는 영역
1. Bounded Context (바운디드 컨텍스트)
하나의 모델과 하나의 유비쿼터스 언어가 일관되게 통하는 경계입니다. DDD의 가장 중요한 개념
왜 필요한가? 시스템이 커지면 같은 단어가 다른 의미를 갖게 됩니다.
예시 — 이커머스에서 "상품(Product)"이라는 단어:
- 카탈로그 컨텍스트: 이름, 설명, 카테고리, 이미지, 가격 — "고객에게 보여주는 상품 정보"
- 재고 컨텍스트: SKU, 창고 위치, 수량 — "물리적으로 관리되는 단위"
- 주문 컨텍스트: 상품 ID, 주문 시점 가격, 수량 — "주문에 담긴 스냅샷"
- 배송 컨텍스트: 무게, 부피, 포장 단위 — "운반 가능한 형태"
같은 "상품"인데 컨텍스트마다 모델이 완전히 다르다. 이걸 하나의 거대한 Product 클래스로 합치려고 하면 필드가 50개 넘는 괴물이 된다. 그래서 컨텍스트별로 경계를 긋고 각자의 모델을 갖게 한다.
*서브도메인과의 차이:
- 서브도메인 = Problem Space, 비즈니스 영역의 구분
- 바운디드 컨텍스트 = Solution Space, 모델/코드의 경계
이상적으로는 1:1이지만 현실에서는 어긋나는 경우도 많습니다.
2. Context Map (컨텍스트 맵)
여러 바운디드 컨텍스트가 서로 어떤 관계로 연결되는지 그린 지도. 단순한 시스템 구조도가 아니라 팀 간 관계와 협력 방식까지 표현하는 게 특징입니다. (콘웨이의 법칙 => "소프트웨어 구조는 팀의 조직도와 똑같이 만들어진다")
3. 컨텍스트 간 통합 패턴
두 컨텍스트가 만났을 때 어떻게 연결할지에 대한 카탈로그입니다.
Partnership (파트너십)
- 두 팀이 운명 공동체. 함께 성공하거나 함께 실패.
- 양쪽이 긴밀하게 협력해서 인터페이스를 같이 진화시킴.
- 예: 주문팀과 결제팀이 새 결제 방식을 같이 만들 때.
Shared Kernel (공유 커널)
- 두 컨텍스트가 모델의 일부를 공유.
- 공유된 부분은 양쪽 팀의 합의 없이 절대 변경 불가.
- 강결합이라 신중하게 써야 함. 보통 핵심 도메인 객체나 공유 라이브러리.
- 예: "Money", "Address" 같은 값 객체.
Customer-Supplier (고객-공급자)
- 한쪽이 공급자(Upstream), 다른 쪽이 고객(Downstream).
- 고객이 요구사항을 내면 공급자가 일정에 반영해줌. 협상 가능한 관계.
- 예: 주문(고객)이 결제(공급자)에게 "이런 응답 필드가 필요해요" 요청 → 결제팀이 다음 스프린트에 반영.
Conformist (순응자)
- 다운스트림이 업스트림 모델을 그대로 따라감. 협상력 없음.
- 외부 시스템이나 사내 다른 팀이 우리 요청을 안 받아줄 때.
- 예: 외부 PG사 API 모델을 그대로 우리 도메인에 받아들이는 경우.
Anti-Corruption Layer / ACL (부패 방지 계층)
- 외부 모델이 우리 도메인을 오염시키지 못하도록 번역 계층을 둠.
- 외부의 이상한 모델·용어가 우리 코드에 스며드는 걸 막음.
- DDD에서 매우 자주 쓰이는 패턴.
- 예: 레거시 시스템과 통신할 때, 레거시의 USR_INFO_TBL_DTO를 우리 도메인의 Customer로 변환해주는 어댑터 레이어.
- 안드로이드로 비유하면 Repository 패턴에서 외부 API 응답 DTO를 도메인 모델로 매핑하는 매퍼 클래스가 ACL의 역할에 가까워요.
Open Host Service (개방 호스트 서비스)
- 여러 다운스트림이 사용할 수 있도록 잘 정의된 프로토콜·API를 공개.
- 공급자가 일관된 API를 제공해서 각 고객별로 커스텀하지 않음.
- 예: 사내 공통 인증 서비스, 결제 게이트웨이.
Published Language (공표된 언어)
- Open Host Service와 짝을 이룸. 모두가 따르는 표준 데이터 형식.
- 예: REST API의 JSON 스키마, ProtoBuf 정의, 산업 표준(HL7, FHIR 등).
Separate Ways (각자의 길)
- 통합하지 말고 그냥 분리해서 가자.
- 통합 비용이 가치보다 클 때의 의식적 선택.
- 예: 영업팀과 인사팀 시스템처럼 사실상 접점이 없는 경우.
Big Ball of Mud (거대한 진흙 공)
- 경계가 없는 혼돈 상태. 의도된 패턴이 아니라 현재 상태를 직시하는 라벨.
- 컨텍스트 맵에 굳이 표시하는 이유: "이 영역은 진흙공이니 ACL 두고 격리하자" 같은 결정을 위해.
