왜 이 글을 쓰게 되었나
DDD(Domain-Driven Design)는 개발자라면 한 번쯤은 들어봤을 개념이다.
나 역시 이전까지는 DDD를 “도메인을 중심으로 설계하는 방법론” 정도로만 이해하고 있었다.
DDD를 하나의 설계 방법론으로서 개념적으로만 이해하고 있었으며, 실제 서비스 코드에 적용된 사례를 직접 다뤄본 경험은 없었다.
그러던 중 현재 회사에 합류하면서, DDD를 기반으로 설계된 서비스를 처음으로 접하게 되었다.
코드를 처음 열어봤을 때 느낀 감정은 ‘이해가 된다’기보다는, 솔직히 말해 ‘익숙하지 않다’에 가까웠다.
레이어는 분리되어 있었고, 파일 구조도 분명 의도가 있어 보였지만, 한눈에 로직이 들어오지는 않았다.
이 글은 DDD를 깊이 있게 설명하려는 목적이 아니다.
DDD를 말로만 알고 있던 한 백엔드 개발자가, 실제 서비스 코드 속에서 DDD를 처음 마주하며 느꼈던 혼란과 이해의 과정을 정리해보고자 한다.
비슷한 단계에 있는 개발자들에게는 공감의 기록이 되기를, 이미 DDD를 사용하고 있는 팀에게는 하나의 관찰 사례가 되기를 바란다.
DDD를 “개념”으로만 알고 있던 시절
이전까지의 개발 경험에서 DDD는 주로 문서나 발표를 통해 접한 개념에 가까웠다.
도메인을 중심으로 모델을 설계하고, 비즈니스 규칙을 코드에 명확히 표현한다는 방향성 자체에는 공감했지만, 실제 프로젝트에 적용해본 경험은 없었다.
여태까지 경험했던 서비스 구조는 비교적 단순한 계층형 아키텍처를 기반으로 하고 있었다.
Controller에서 요청을 처리하고, Service에서 비즈니스 로직을 수행하며, Repository를 통해 데이터에 접근하는 형태였다.
이러한 구조는 빠르게 기능을 구현하고 전체 흐름을 파악하는 데에는 큰 어려움이 없었고, 비교적 익숙한 방식이었다.
DDD를 도입하지 않았던 이유는 기술적인 거부감 때문이라기보다는, 적용에 대한 명확한 필요성과 기준이 없었기 때문이다.
Aggregate, Entity, Value Object와 같은 개념들은 알고 있었지만, 이를 실제 코드 구조로 어떻게 녹여내야 하는지에 대한 감각은 부족했다.
결과적으로 DDD는 “언젠가 필요하면 도입할 수 있는 설계 방법론” 정도로 인식되고 있었다.
이러한 상태에서 DDD가 적용된 서비스를 처음 접하게 되면서, 기존에 알고 있던 구조와는 다른 설계 방식과 코드 분리가 자연스럽게 대비되기 시작했다.
처음 마주한 DDD 기반 서비스 구조
처음 코드를 살펴보며 가장 먼저 확인한 부분은 전체적인 프로젝트 구조였다.
서비스는 기능 단위가 아닌 도메인 중심으로 구성되어 있었고, 각 도메인은 다시 역할에 따라 명확하게 분리되어 있었다.
전반적인 구조는 Application, Domain, Infrastructure 레이어로 나뉘어 있었으며, 각 레이어는 서로 다른 책임을 가지도록 설계되어 있었다.
Application 레이어는 유스케이스 단위의 흐름을 담당하고 있었고, Domain 레이어에는 핵심 비즈니스 규칙과 도메인 모델이 위치해 있었다.
Infrastructure 레이어는 데이터베이스 접근이나 외부 시스템 연동과 같은 기술적인 세부 구현을 담당하고 있었다.
NestJS를 사용하고 있었지만, 프레임워크의 기본 구조를 그대로 따르기보다는 DDD의 레이어 구분을 우선적으로 반영한 형태였다.
Controller는 비교적 얇게 유지되어 있었고, 대부분의 비즈니스 흐름은 Application 레이어의 구성 요소를 통해 처리되고 있었다.
또한 Domain 레이어는 NestJS에 대한 의존성을 가지지 않도록 구성되어 있어, 프레임워크와 도메인 로직이 직접적으로 결합되지 않도록 설계되어 있었다.
이러한 구조는 각 구성 요소의 역할과 책임을 명확히 드러내고 있었지만, 동시에 전체 흐름을 따라가며 이해하는 데에는 일정한 학습 비용이 필요했다.
단일 Service 파일 안에서 모든 로직을 확인하던 방식과 달리, 하나의 기능이 여러 레이어에 걸쳐 분산되어 있었기 때문이다.
그럼에도 불구하고 코드 전반에서는 “왜 이렇게 나누었는지”에 대한 의도가 비교적 일관되게 유지되고 있었고, 이는 이후 코드를 이해하는 데 중요한 단서가 되었다.