
DDD (Domain Driven Design): 개발자가 비즈니스 언어를 배워야 하는 이유
DDD는 단순히 'Repository 패턴을 쓰는 것'이 아닙니다. 복잡한 비즈니스 문제를 해결하기 위해 개발자와 기획자가 같은 언어(Ubiquitous Language)를 사용하고, 거대한 시스템을 'Bounded Context'로 나누어 정복하는 전략적 설계 방법론입니다.

DDD는 단순히 'Repository 패턴을 쓰는 것'이 아닙니다. 복잡한 비즈니스 문제를 해결하기 위해 개발자와 기획자가 같은 언어(Ubiquitous Language)를 사용하고, 거대한 시스템을 'Bounded Context'로 나누어 정복하는 전략적 설계 방법론입니다.
로버트 C. 마틴(Uncle Bob)이 제안한 클린 아키텍처의 핵심은 무엇일까요? 양파 껍질 같은 계층 구조와 의존성 규칙(Dependency Rule)을 통해 프레임워크와 UI로부터 독립적인 소프트웨어를 만드는 방법을 정리합니다.

왜 CPU는 빠른데 컴퓨터는 느릴까? 80년 전 고안된 폰 노이만 구조의 혁명적인 아이디어와, 그것이 남긴 치명적인 병목현상에 대해 정리했습니다.

ChatGPT는 질문에 답하지만, AI Agent는 스스로 계획하고 도구를 사용해 작업을 완료한다. 이 차이가 왜 중요한지 정리했다.

결제 API 연동이 끝이 아니었다. 중복 결제 방지, 환불 처리, 멱등성까지 고려하니 결제 시스템이 왜 어려운지 뼈저리게 느꼈다.

개발자라면 이런 경험이 한 번쯤 있을 겁니다.
기획자는 "주문 취소"라고 말하는데, 개발자는 deleteOrder()라고 코드를 짭니다.
기획자가 "부분 환불"을 이야기하면, 개발자는 updateOrderPrice()를 찾습니다.
시간이 지나면 코드는 점점 비즈니스 용어와 멀어지고, 개발자들만 아는 기술 용어(DTO, Manager, Processor)로 뒤덮입니다.
나중에 기획자가 와서 "주문 취소 정책이 바뀌었어요"라고 하면, 개발자는 멘붕에 빠집니다.
"잠시만요, 그거 deleteOrder 함수 고치면 User 테이블이랑 Payment 테이블 참조 무결성 에러 터지는데요..."
이 거대한 소통의 장벽을 부수기 위해 탄생한 것이 바로 도메인 주도 설계(Domain-Driven Design, DDD)입니다. 에릭 에반스(Eric Evans)가 제안한 이 방법론의 핵심은 "코드를 비즈니스 멘탈 모델과 일치시켜라"입니다.
DDD의 시작이자 끝입니다. "개발자, 기획자, 디자이너, 도메인 전문가가 모두 같은 용어를 써라."
enrollCourse()가 있어야 합니다. insertStudentCourseMap()이 아니라요.addToCart()가 있어야 합니다. createCartItem()이 아니라요.이 언어는 단순히 변수 이름을 맞추는 게 아닙니다. 회의실에서 쓰는 말, 문서에 적힌 말, 그리고 코드에 적힌 말이 100% 일치해야 합니다. 통역사가 필요 없는 상태, 그것이 유비쿼터스 언어입니다.
DDD는 크게 전략적 설계와 전술적 설계로 나뉩니다. 사람들은 보통 전술적 설계(Entity, Value Object)에 집착하지만, 훨씬 중요한 건 전략적 설계입니다.
거대한 시스템을 하나의 모델로 표현하는 건 불가능합니다.
예를 들어 Product라는 단어를 봅시다.
Product는 "창고 위치, 재고 수량, 입고일"이 중요한 데이터입니다.Product는 "상품명, 가격, 할인율, 상세 설명"이 중요한 데이터입니다.Product는 "무게, 부피, 파손 주의 여부"가 중요한 데이터입니다.이걸 하나의 Product 클래스에 다 때려 넣으면 300줄짜리 괴물이 탄생합니다.
DDD는 이렇게 하지 말라고 합니다. 대신 문맥(Context)을 나눕니다.
InventoryItem (재고 수량, 위치)DisplayProduct (상품명, 이미지)Parsel (무게, 주소)각 컨텍스트 안에서는 용어의 의미가 명확하고 통일됩니다. 그리고 컨텍스트 간에는 명시적인 인터페이스(API, Event)를 통해서만 소통합니다. 이것이 바로 마이크로서비스 아키텍처(MSA)의 이론적 기반이 됩니다.
컨텍스트를 나눴다면 이들이 어떻게 관계를 맺는지 정의해야 합니다.
이 지도가 없으면 MSA는 "분산된 진흙탕(Distributed Big Ball of Mud)"이 됩니다.
컨텍스트를 나눴으면, 그 안을 채워야겠죠. 여기서 우리가 흔히 아는 패턴들이 나옵니다.
User, Order) 속성이 변해도 ID가 같으면 같은 객체입니다. 생명주기(Lifecycle)가 있습니다.Money, Address, Color) 1000원은 다 같은 1000원입니다. 불변(Immutable)으로 만들어야 합니다. VO를 적극 활용하면 "Side Effect"를 원천 차단할 수 있습니다.연관된 객체들의 묶음입니다. 데이터 무결성을 보장하는 단위입니다.
예를 들어 Order(주문)와 OrderLineItem(주문 항목)이 있다고 칩시다.
OrderLineItem만 따로 존재할 수 있나요? 주문 없는 주문 항목은 말이 안 됩니다.
이때 Order가 애그리거트 루트(Aggregate Root)가 됩니다.
// Aggregate Root를 통해서만 내부 조작 가능
class Order {
private List<OrderLineItem> items;
public void addLineItem(Product product, int quantity) {
if (items.size() >= 10) {
throw new BusinessException("상품은 최대 10개까지만 담을 수 있습니다.");
}
items.add(new OrderLineItem(product, quantity));
}
}
외부에서는 order.addLineItem()만 호출할 수 있습니다. order.getItems().add(...) 처럼 내부에 직접 접근하는 것을 막아야 합니다. 이것이 캡슐화입니다.
어떤 로직은 엔티티 하나에 넣기 애매할 때가 있습니다.
예를 들어 "환율 계산을 해서 결제 금액을 확정한다"는 로직은 Payment에 넣어야 할까요, Currency에 넣어야 할까요?
둘 다 어색합니다. 이럴 때 도메인 서비스를 만듭니다. 순수하게 도메인 로직만 수행하는 무상태(Stateless) 객체입니다.
class CurrencyConversionService {
Money convert(Money amount, Currency from, Currency to) {
// 외부 환율 API를 호출하거나 DB를 조회해서 계산
// 도메인 로직의 순수성을 지키는 역할
}
}
DDD를 한다고 해서 반드시 JPA를 써야 하거나 Event Sourcing을 해야 하는 건 아닙니다. 기술 스택은 도메인 모델을 구현하기 위한 도구일 뿐입니다.
가장 중요한 건 "비즈니스의 복잡성을 코드에 녹여내는 태도"입니다.
DDD는 어렵습니다. 학습 곡선이 가파릅니다. 하지만 복잡한 비즈니스 문제를 소프트웨어로 해결해야 하는 순간, DDD는 가장 강력한 무기가 되어줄 것입니다.
많은 사람들이 "MSA를 하려면 DDD를 해야 한다"라고 말합니다. 왜 그럴까요? 마이크로서비스를 나누는 기준이 바로 Bounded Context이기 때문입니다.
모놀리스 시스템에서도 DDD는 유효합니다.
코드를 물리적으로 나누지 않더라도, 논리적으로 Package나 Module을 통해 Context를 분리할 수 있습니다.
이것을 "모듈러 모놀리스(Modular Monolith)"라고 부릅니다.
처음부터 MSA로 시작하면 인프라 복잡도 때문에 망할 확률이 높습니다.
먼저 모듈러 모놀리스로 도메인 경계를 명확히 한 뒤, 트래픽이 터지는 모듈만 떼어내는 것이 정석입니다.