Domain-Driven-Design 의 첫인상과 오해
저희 팀은 프로덕트가 점차 비대해지며 추가되는 도메인끼리 얽히고 얽히는 빅볼(Big-Ball) 구조가 되는 것에 대비하고자 이번 정산 서비스는 DDD(Domain-Driven-Design) 아키텍처를 체택했습니다. 또한 기존의 core bank service 도 DDD 구조로 리팩토링 할 계획에 있는데요,
이러한 결정이 내려지고 처음 DDD를 접했을 때 느낀 첫인상은 말 그대로 Domain 특별 대우 아키텍처였습니다.도메인을 직역하자면 "영역" 으로, 서비스 안에서 핵심이되는 비즈니스 로직을 도메인(영역) 단위로 분리하여 각각의 도메인이 책임지는 비즈니스 로직은 해당 도메인 안에서 해결되어야합니다.
결국엔 ‘도메인 중심의 객체지향 클린아키텍처구나’ 가 저의 DDD 에 대한 첫인상이었습니다.
하지만, DDD 가 갖는 진정한 의미는 디자인 패턴(Design Pattern) 또는 아키텍처(Architecture) 와 같이 프로그래머들을 위한 방법론적인 관점이 아니라, 인문학 관점이 필요한 예술의 영역의 전략적 설계에 있습니다.
결론부터 말하자면, DDD는 개발자들의 전유물인 디자인 패턴도, 코드의 유지보수에 도움이 될 아키텍처도 아닙니다.
DDD는 개발자 뿐만 아니라 프로그램 또는 비즈니스 서비스에 속해있는 모든 인력들이 같은 의미로 소통할 수 있는 도면을 그리며, 그것을 코드에 녹여내는 예술의 영역입니다.
전략적 설계의 DDD
DDD는 Domain 을 정의하고 관심사를 분류하는 ‘전략적 설계 관점’과, 그 설계를 직접 구현하는 과정의 ‘전술적 패턴 관점’으로 나누어 볼 수 있습니다.
쉽게 말하면 앞서 말했던 도면을 그리는 설계의 측면에서의 DDD 와 그 도면을 코드로 녹여내는 기술의 측면에서의 DDD 로 나누어 볼 수 있는데요,
그 중 Domain-Driven-Design 의 본질은 전략적 설계에서 나타납니다.
![](https://blog.kakaocdn.net/dn/CKNOB/btsHqPB5AMF/RmWJaFOwtlyUZ7yRljTI2K/img.png)
프로덕트를 출시하는 과정은 정말 길고 복잡한 과정입니다... 저희 포인트3 또한 모든 팀원들이 불철주야 노력하고있는데요,
프로덕트 출시 과정에는 구상, 설계, 구현, 배포, 영업 등의 각각의 프로세스로 나눈다면, 각각의 업무는 올림픽 육상 경기 종목들과 같아서 각각의 과정에서 생각해야 될 부분이 조금씩 다르고, 그러다보니 전문 분야가 갈리게 되고, 특정 분야의 전문 인력이 필요합니다. 저희 팀만 보더라도 개발, 디자인, 기획 및 마케팅 으로 전문 분야가 나뉘네요.
나아가 그 전문 인력이란 도메인만 전문적으로 분석하는 도메인 전문가가 될 수도 있고, 직접 코드를 구현하는 엔지니어가 될 수도 있고, 개발에 참여하지 않더라도 우리팀 프로덕트에 대한 전반적인 이해가 필요한 기획자와 마케터가 될 수도 있습니다.
전략적 설계의 DDD 는 이 과정에서 오는 전문 인력들의 지식 통합과 각각의 프로세스에서의 도메인에 대한 간극을 좁히는데 초점이 맞추어집니다. 이것이 앞서 비유한 “서비스에 속해있는 모든 인력들이 같은 의미로 소통할 수 있는 도면 그리는 것” 이죠.
정리하면 DDD의 전략적 설계란, 개발 프로세스에서 각각의 팀원들이 보편적인 표현으로 도메인에 대해 이해하고 정의하는 과정입니다.
DDD에서 말하는 중요한 개념인 유비쿼터스 렝귀지(Ubiquitous Language)가 바로 전략적 설계 과정의 '보편적인 표현'인 것입니다.
전략적 설계 과정
![](https://blog.kakaocdn.net/dn/1esA4/btsHqxaByeE/F8QBtQoHkUemMLFuGrZkl0/img.png)
크게 전략적 설계는 위 그림과 같은 과정을 따르게 됩니다. 비즈니스 도메인에서 문제점을 찾고, 이를 해결하기 위해 문제 도메인을 구성합니다. 만약 문제로 정의한 도메인이 해결된다면, 해결 도메인으로 바뀌는 것이고요.
이 때, 비즈니스 도메인은 현실 세계에서 실제 회사가 고객에게 제공하는 서비스 영역이고, 이 서비스 영역에서 소프트웨어 세계에서 즉 코드를 통해 해결할 수 있는 도메인을 문제 도메인으로 정의합니다.
따라서 비즈니스 도메인이 문제 도메인과 같을 수도 있습니다. 실제 제공하는 서비스가 모두 소프트웨어로 구현되는 경우가 이에 해당하게 되는데, 우리팀의 경우도 비즈니스 도메인과 문제 도메인이 동일시 된다고 볼 수 있겠네요.
이렇게 문제 도메인을 정의했으면 해결해야될 핵심이 되는 문제를 도메인 단위로 정제하고 해결하는 과정이 전체적인 흐름이 됩니다.
저희 팀이 “실시간 정산" 이라는 비즈니스 도메인을 결제 서버를 통해 문제 도메인으로 정제하여 코드를 통해 해결 도메인으로 바꾼 상황을 생각하면 되겠네요.
즉 전략적 설계는 소프트웨어로 어떠한 문제를 해결하여 가치를 창출하는 과정이 되겠습니다.
이제 전략적 설계의 구체적인 방법 중 하나인 이벤트 스토밍에 대해서 알아보기전에, 알아야 할 개념들에 대해서 먼저 정리해보았습니다.
Aggregate란, 하나의 도메인을 대표하는 집합단위입니다.
객체지향 프로그래밍에서 Aggregate는 논리적으로 관련된 객체들을 그룹화하여 하나의 묶음으로 다루는 단위입니다. 이를 DDD의 전략적 설계 관점으로 바라보면, 하나의 관심 영역 즉 도메인이라고 생각하면 됩니다.
개별 객체 수준의 모델만을 참고하여 도메인을 분석하게 되면 무엇이 중요하고 핵심인지 한눈에 파악하기가 힘들지만, 애그리거트 단위로 도메인을 먼저 이해한 후 객체 수준의 모델을 바라보게 된다면이해하기가 훨씬 쉽게 됩니다.
Bounded-Context란, 해결 공간에서 모델의 경계입니다.
핵심 문제가 되는 메인 도메인(Main Domain)과 그 메인 도메인에 뒤따라오는 문제를 다루는 서브 도메인(Sub Domain)의 묶음으로 구성되는데, 이를 쉽게 설명하면 각각의 이벤트 도메인을 통해 문제를 해결하는 하나의 흐름 단위가 되겠습니다.
이 때, 흐름(Context)의 경계(Bounded)가 다르면, 각 경계에서 유비쿼터스 언어도 달라지게 됩니다.
각각의 문제 흐름에서 보편적으로 표현하는 방법이 다른것이죠.
우테톡에서 재치있게 설명한 비유를 참고하였습니다.
![](https://blog.kakaocdn.net/dn/3Qbpc/btsHpbUblYI/olUbjblx7fYyoBkzJKkmUk/img.png)
피자를 음식으로 먹는 '소비자의 입장에서의 피자'와 피자를 버리는 '청소부의 입장에서의 피자' 는 각각 다른 의미의 피자입니다.
소비자가 피자를 먹는 것과 청소부가 피자를 버리는 것은 다른 Bounded-Context이고, 각각의 바운디드에서의 피자는 서로 다른 유비쿼터스 언어인 것입니다.
저희 팀에서도 ‘포인트3의 프로덕트를 사용하는 고객' 과 그 ‘고객사의 고객' 에 대한 정의에서 곤란을 겪은 적이 있는데, 이를 각각의 다른 Context-Bounded로 나누었다면, 같은 Client 이지만 다른 유비쿼터스 언어가 되는 것이죠.
이렇듯 Bounded-Context는 모델의 무결성을 위한 경계입니다.
이를 개발 용어로 비유하자면 보장되는 하나의 트랜잭션 이라고 할 수도 있겠네요.
Context-Map 이란, 앞서 말한 Bounded-Context 끼리의 Mapping 관계를 그린 다이어그램입니다.
Bounded-Context 는 문제를 해결하는 흐름의 경계 인데, 각각의 경계는 사이에 서로 데이터를 주고 받는 등 소통이 팰요한 경우가 있습니다.
따라서 이렇게 Bounded-Context 끼리의 소통을 시각화하여 맵으로 나타낸 것이 Context-Map 입니다.
이벤트 스토밍
이벤트 스토밍은 단순히 DDD 만을 위한 도메인 정리 방법이 아닌, 이벤트 주도의 마이크로서비스를 잘 뽑아내기 위한 방법론 중 하나입니다.
이벤트 스토밍은 구체적인 도메인 이벤트(Domain-Event)에서 추상적인 바운디드 컨텍스트(Bounded-Context)로 나누는 Bottom-Up 방식인데요,
준비물은 큰 화이트 보드와 여러 색상의 포스트잇입니다.
이 때, 각각의 다른 색상의 포스트잇은 다음과 같은 의미를 내포합니다.
![](https://blog.kakaocdn.net/dn/bZvj7h/btsHqjqa4Dq/QMD1kqbYkK5NkjWrvKXLH1/img.png)
이제 도메인 전문가, 개발자, 마케터 그외 개발에 필요한 인력 모두가 화이트 보드에 종이를 붙이며 골똘히 생각하면 된다.
![](https://blog.kakaocdn.net/dn/bdLjeU/btsHqdcFjIj/NpZJc7EYb4KAPqP0wdg1y0/img.png)
1. 이벤트(Event) 도출
주황색으로 표현되는 이벤트는 말 그대로 발생하는 일을 뜻합니다. 이는 하나의 메인 도메인이 될수도, 서브 도메인이 될 수도 있겠죠.
이벤트를 먼저 도출하는 이유는 전체적인 프로세스 흐름의 청사진이 될 수 있고, 또한 시스템과 시스템간에 발생하는 상호작용을 어렴풋이 예측 할 수 있기 때문입니다.
![](https://blog.kakaocdn.net/dn/bdRtEq/btsHqzGiOlw/cHQkpdRCKb2OGVoxgybOPk/img.png)
2. 커맨드(Command) 도출
파란색으로 표현되는 커맨드는 트리거(trigger)와 같이 사용자 또는 그 외의 주체가 일으키는 액션을 뜻합니다. 이는 다른 의미로는 의사 결정이라고 할 수 있습니다.
따라서 커맨드는 이를 실행하는 주체인 액터가 따라오게 됩니다.
![](https://blog.kakaocdn.net/dn/l6LGT/btsHqratsYT/pDEtsbyvLzxK42MO5HJRO1/img.png)
3. Policy 도출
다음 라일락 색에 주목해봅시다.
가정 시나리오는 다음과 같은데요, "유저가 주문을 하면 배송이 시작된다."
그렇다면 'start delivery' 는 커맨드라고 하기엔 액터가 없는 것입니다.
'OrderPlaced' 이벤트에 의해 'start delivery' 커맨드가 실행되는데, 이렇게 다른 이벤트 결과가 다른 이벤트의 트리거가 되는 경우를 Policy 로 정의합니다.
이는 비즈니스로 정의되어있는 자동화된 프로세스로 생각할 수 있습니다.
![](https://blog.kakaocdn.net/dn/XZIrx/btsHqzsMUSL/vKtQqSbsHIuvl9K9pzagK0/img.png)
4. Aggregate 도출
다음 살구색으로 표현되는 Aggregate는 input 과 output 즉 cammand 와 event 사이에 들어가는 하나의 시스템을 뜻하고, 이 시스템은 Aggregate를 통해 state change가 발생하는 동안의 도메인 로직, 데이터베이스 로직 등의 작업이 들어갈 수 있습니다. 앞서 설명했던 도메인을 대표하는 집합을 정의하는 것입니다.
여기에서 여러개의 command-event 가 묶일 수도 있다.
정리하면 cammand 와 event 사이의 발생하는 상태 변경을 관리하는 시스템입니다.
![](https://blog.kakaocdn.net/dn/9uK3k/btsHpRnomCw/cCHsNtHEBZS8N6r3kn96e0/img.png)
5. Bounded-Context
Aggregate 를 정의한 과정까지 마치면 도메인 간의 연관성이 보이고 분리가 가능해집니다. 즉 Bounded-Context 를 정할 수 있게 되는 것입니다.
다음과 같이 main 과 sub 도메인으로 나누면 서로의 의존이 줄고 이는 확장과 유지 및 보수에 유리해지는 구조가 되는 것이죠.
![](https://blog.kakaocdn.net/dn/cA1k0y/btsHqluRCsm/sunldyUIwHtLSoOTFK79K1/img.png)
6. Context-Map
이 과정을 각각의 Context-Bounded 에 적용하여 찾아내면, 이후에 Bounded 간의 관계성이 보이고, 이 관계성을 다이어그램으로 표현해주면 최종 단계인 Context-Map 까지 완성됩니다.
![](https://blog.kakaocdn.net/dn/7hy5U/btsHqiSoldc/yvDgVT3RKphytlRzlAsKGK/img.png)
마치며
지금까지 전략적 설계 관점의 DDD에 대해서 정리해 보았습니다.
저희는 DDD 를 사용한 전략적 설계 과정을 통해 각 도메인이 담당하는 주요 서비스에 대해 정리(by Bounded-Context) 할 수 있으며, 도메인 각각의 의존성을 낮추고 유지 보수에 유리해질 뿐더러(by Context-Map)
DDD 의 본질인 개발 과정에서 참여하는 여러 분야의 팀원들이 보편화된 언어를 통해 같은 목표의식을 갖고 협업할 수 있습니다. (by Ubiquitous Language)