모두를 위한 DDD

도메인 모델, 보편 언어, 바운디드 컨텍스트를 포함한 DDD의 전략 개념들을 알아보자

Do not index
Do not index

들어가며

대부분의 소프트웨어는 현실에 존재하는 특정한 문제를 해결하기 위해 존재합니다. 만약 해결하려는 문제가 아주 복잡하다면 그 문제를 해결하는 소프트웨어도 복잡해질 수 밖에 없습니다. 개발자는 소프트웨어를 잘 설계하여 이러한 복잡성을 통제해야 합니다. 설계 요소에는 어떤 데이터를 어떤 데이터베이스에 어떻게 저장할 지, 네트워크를 어떻게 설계할 지, API를 REST, GraphQL 중 어떤 형식으로 설계할 지 등 기술과 관련된 것들이 있습니다.
도메인 주도 설계(Domain Driven Design DDD)는 가장 중요한 설계 요소는 기술적인 것이 아니라, 사용자의 활동이나 업무에 해당하는 비즈니스 도메인 자체인 자체라고 주장합니다. 비즈니스 도메인을 깊게 이해하고 이로부터 좋은 도메인 모델을 정제해야 성공적인 설계를 통해 도메인의 복잡성을 잘 다룰 수 있습니다.
그렇다고 DDD가 개발자들을 위한 것만은 아닙니다. 소프트웨어 제품을 개발하는데 개발자 뿐만 아니라 기획자, 디자이너 및 기타 다양한 이해관계자가 많아지면서 이들간의 원활한 커뮤니케이션이 매우 중요해졌습니다. DDD를 통해 팀 전체가 비즈니스 도메인을 이해하는 것의 중요성을 인지하고 도메인 모델을 공유하며, 보편 언어로 소통함으로써 프로젝트의 성공에 기여할 수 있습니다.
일반적으로 DDD를 검색해 보면 하면 엔티티, 값 객체, 애그리게잇, 도메인 서비스 등 구현에 관련된 전술 개념들에 관한 글들이 많습니다. 물론 이들도 DDD의 일부이고 훨씬 더 실체화 된 개념이기 때문에 받아들이고 적용하기 쉽지만, DDD의 본질은 좋은 도메인 모델을 설계하는데 있습니다. 이 글에서는 좋은 도메인 모델을 설계하고 유지하는 방법인 DDD의 전략 개념에 초점을 맞춰 DDD를 설명하려고 합니다. 따라서 개발자 뿐만 아니라 소프트웨어 제품 개발에 참여하는 모든 사람들이 글의 대상 독자가 될 수 있습니다.
이 글의 구성은 에릭 에반스의 도메인 주도 설계를 바탕으로 하였습니다.
이 글의 구성은 에릭 에반스의 도메인 주도 설계를 바탕으로 하였습니다.

핵심 개념

도메인 모델

왼쪽은 한반도의 지형을 나타낸 지도이고, 오른쪽은 한반도의 주요 광물 자원 분포를 나타낸 지도입니다. 모두 실제로 현실에 존재하는 한반도라는 대상(도메인)을, 풀고자 하는 문제에 따라 단순화(추상화)하여 나타낸 것(모델) 입니다.
한반도의 지형 기복 및 단면도
출처. 대한민국 국가지도집
한반도의 지형 기복 및 단면도 출처. 대한민국 국가지도집
한반도의 주요 금속 광물 자원 분포도
출처. 대한민국 국가지도집
한반도의 주요 금속 광물 자원 분포도 출처. 대한민국 국가지도집

도메인 모델을 통해 도메인 바라보기

모든 제품은 제품을 사용하는 사용자의 활동이나 관심사와 관련되어 있습니다. 사용자가 제품을 사용하는 대상 영역이 바로 제품(소프트웨어)의 도메인 입니다. 닥터차 서비스의 경우에는 차량 정비/수리 영역이 도메인이 됩니다. 사용자에게 도움이 되는 제품을 만들기 위해 개발자는 도메인과 관련된 지식 체계에 집중해야 합니다. 그러나 개발자가 배워야 할 도메인 지식이 너무 어렵거나 많을 수 있고, 그 중 일부는 소프트웨어가 해결하고자 하는 문제와 관련이 없을 수도 있습니다. 예를 들어 외장 수리 견적 서비스를 만드는 메이커는 덴트 작업의 종류와 비용에 대해서는 알아야겠지만, 덴트에서 사용되는 퍼티(자동차 외판의 굴곡을 메울때 사용하는 제품)의 구성 성분까지 알아야 할 필요는 없습니다. 따라서 메이커는 풀고자 하는 문제에 관련된 정보를 단순화하고 구조화하여 도메인 모델을 만들고, 모델을 토대로 도메인을 이해하고 문제 자체에 집중해야 합니다.

도메인 모델의 예시

오토피디아 부품유통팀의 업무를 예시로 도메인 모델을 구축하는 과정을 조금 더 구체적으로 설명해 보겠습니다. 부품유통팀에서는 사고 차량의 사진을 보고 차량을 수리하는데 필요한 부품 목록을 찾아 견적을 작성합니다. 그리고 이 견적의 부품 목록을 여러 부품 공급처(도매상)들에서 매입합니다.
부품유통팀에서 작성한 부품 견적을 가장 많은 이익을 남기도록 매입하는 방법을 찾아주는 제품을 만든다고 생각해 봅시다. 이 도메인에는 다음과 같은 사건들이 발생합니다.
  • 동일한 부품을 여러 공급처에서 서로 다른 가격으로 제공.
  • 각 부품은 센터가(정가)가 존재하며, 공급처에서는 정가보다 저렴한 가격에 부품을 제공.
  • 보험사에 매입 내역의 정가를 기준으로 금액을 청구하면, 보험사는 할인 계약에 따라 청구액을 할인하여 금액을 지급.
    • 할인 계약은 보험사 별로 맺어지며, 부품을 매입한 매입처에 따라 다른 할인율을 적용하여 지급액을 계산.
예를 들어 센터가가 1만원인 부품 A가 있습니다. 이 부품 A 10개를 포함하는 견적이 작성 되었습니다. 공급처1에서는 부품 A를 개당 5천원에, 공급처B에서는 부품 B를 개당 8천원에 제공합니다. 이 견적은 햇살화재에서 처리될 예정입니다. 우리가 햇살화재와 맺은 할인 계약은 공급처1에서 매입 시에 40%, 공급처2에서 매입 시에 5%의 할인율을 적용합니다. 따라서 부품 A 10개를 공급처1에서 매입하면 매입액이 5만원, 지급액이 6만원이므로 1만원의 이익이, 공급처2에서 매입하면 매입액이 8만원, 지급액이 9만5천원이므로 1만5천원의 이익이 발생하므로 공급처2에서 매입해야 합니다.
공급처 A
공급처 B
부품 A의 판매가
5,000원
8,000원
햇살화재의 공급처 별 할인율
40%
5%
부품 A 10개 매입액
50,000원
80,000원
햇살화재의 부품 A 10개에 대한 지급액
100,000원 * 0.6 = 60,000원
100,000원 * 0.95 = 95,000원
순이익
60,000원 - 50,000원 = 10,000원
95,000원 - 80,000원 = 15,000원
notion image
간단한 예시를 들었지만, 한 견적에 몇십개의 부품이 포함될 때도 있고, 한 공급처의 특정 부품에 대한 재고가 부족할 수도 있으며, 부품의 센터가와 공급처들의 판매 가격이 계속 변경되는 점을 고려하면 매번 수동으로 최적의 매입 방법을 찾는 것이 쉽지 않을 것입니다.
도메인으로부터 주요 개념(객체)을 찾아 정리해 봅시다. 각 객체는 속성(프로퍼티)와 행동(메서드)를 가질 수 있습니다.
  • 부품: 부품코드, 센터가.
  • 공급처: 공급처ID.
  • 재고: 부품코드, 공급처ID, 가격.
  • 견적: 견적 항목 목록. 보험사ID.
    • 견적 항목: 부품코드, 수량.
  • 매입 방법: 매입 항목 목록.
    • 매입 항목: 부품코드, 수량, 매입가, 공급처ID.
  • 보험사: 보험사ID. 할인 계약. 청구(매입내역으로부터 지급액을 반환).
    • 할인 계약: 보험사 ID, 할인 정책 목록.
      • 할인 정책: 공급처ID, 할인율.
  • 견적 매입 서비스: 매입(견적으로부터 최적의 매입 방법을 반환).
UML 다이어그램 형식으로 표현한 도메인 모델
UML 다이어그램 형식으로 표현한 도메인 모델
도메인의 비즈니스 로직을 객체들의 속성과 행위로 표현할 수 있게 되었습니다. 이제 이 객체들을 코드로 구현하면 제품을 완성할 수 있습니다. 구현 과정에서 엔티티, 값 객체, 애그리게잇 등 DDD의 전술 개념들을 사용할 수 있습니다. 그러나, 이 글에서는 DDD의 전술 개념에 대해서는 다루지 않습니다.

지속적인 학습을 통해 도메인 모델을 발전시키기

도메인 모델을 설계하고 이에 따라 제품을 완성하는 것이 끝이 아닙니다. 누구든 제품을 처음 개발할때는 도메인을 완전히 이해하지 못한 상태에서 시작하게 됩니다. 처음부터 도메인에 대한 모든 정보를 꿰뚫고 있는 개발자는 없습니다. 만약 그렇다고 하더라도 도메인은 시간에 따라 변화하기 때문에 지금 모든 것을 알고 있다고 해서 미래에도 그러리라는 보장은 없습니다. 따라서 처음 만든 도메인 모델은 지금 당장은 괜찮을 수 있으나 시간이 지나면 요구사항을 만족하지 못할 수 있습니다. 따라서 개발자는 도메인으로부터 계속 지식을 얻고 이를 정제하여 도메인 모델에 추가하고 수정해 나가는 과정을 반복해야 합니다.

보편 언어

프로젝트 내에 여러 언어가 공존하면 안 된다

도메인에 대해 전문적인 지식을 가지고 있는 사람을 도메인 전문가라고 부릅니다. 보통 도메인 전문가는 개발자들이 사용하는 기술적인 전문용어를 이해하기 어렵겠지만, 도메인의 전문용어는 다양하게 사용합니다. 반대로 개발자들은 도메인의 전문용어를 이해하기 어렵겠지만, 기술적이고 기능적인 용어들로 제품에 대해 이야기 할 것입니다. 이렇게 공통 언어가 없는 프로젝트에서는 개발자와 도메인 전문가 간의 소통에 번역 과정이 필요합니다. 번역은 정보 전달을 느리고 부정확하게 만듭니다. 이 때문에 팀원들은 도메인에서 유용한 지식을 정제하여 도메인 모델로 만드는데 노력을 쏟지 못하고, 소통을 위한 번역에 너무 많은 노력을 쏟게 됩니다. 누군가 도메인 모델에 위배되는 개념을 제시하더라도 이것이 잘못된 것인지 깨닫기 어렵습니다. 이러한 모든 부작용은 결국 신뢰할 수 없는 제품을 만들어냅니다.
이를 방지하기 위해 팀 내에서는 도메인 모델을 근간으로한 공통 언어, 즉 보편 언어를 구축하고 사용해야합니다. 도메인 전문가와 개발자간의 의사소통에서도, 코드와 문서, 다이어그램에서도 동일한 언어를 사용해야 합니다. 앞서 말했듯이 도메인 모델은 완벽하지 않고 학습을 통해 발전해야 합니다. 도메인 모델을 근간으로한 보편 언어가 제대로 사용되지 않는다면, 이를 도메인 모델에 발전시켜야 할 불완전한 부분이 존재한다는 신호로 볼 수 있습니다. 이는 도메인을 이해하는데 부자연스럽거나 부정확한 용어나 구조가 존재한다는 뜻이고, 보편 언어에 근간이 되는 도메인 모델을 개선해야 한다는 뜻이기 때문입니다.

보편 언어가 제대로 사용 되지 않는 것으로부터 도메인에 숨겨진 개념을 발견한 예시

예를 들어, 견적 기능을 개발하는 프로젝트에서는 한 고객이 한 번에 한 업체에게만 견적을 요청할 수 있었습니다. 따라서,
고객은 업체에게 견적 요청을 남긴다. 고객의 문제 상황에 대한 정보(차종, 사진, 설명 등)은 견적 요청에 포함된다.
라고 도메인 모델을 설계했습니다. 하지만 팀원들끼리 소통할 때도, UI 상의 버튼에도 고객의 문제 상황에 대한 정보가 “견적 요청”이 아닌 “견적 문의”라는 용어로 지칭 되었습니다. 처음에는 모델에 존재하지 않는 용어가 계속 사용되는 것을 경계하여 팀원들에게 모델에 따라 “견적 요청”이라는 용어를 사용해 달라고 요청 드리고, UI 상의 “견적 문의”라는 문구도 “견적 요청”으로 변경하였습니다.
그러나 얼마 뒤 한 고객이 한 번에 여러 업체에게 견적을 요청할 수 있도록 변경 되면서, 고객의 문제 상황에 대한 정보가 견적 요청이라는 객체에서 분리될 필요가 생겼습니다. 새로 생긴 고객의 문제 상황에 대한 객체의 이름이 “견적 문의”로 지정 되었고, 기존의 보편 언어가 제대로 사용 되지 않는 것이 도메인에 존재하지만 모델에는 반영되지 않은 숨겨진 개념을 의미하는 것임을 깨닫게 되었습니다.

어쩔 수 없이 프로젝트 내에 여러 언어가 필요하지 않나요?

몇몇 개발자는 좋은 제품을 개발하기 위해 도메인 모델은 필요하지만 이를 설계하는 것은 개발자의 몫이며, 다음과 같은 표현들로 도메인 전문가에게 이를 공유할 필요가 없다고 주장하기도 합니다.
도메인 모델은 도메인 전문가들에게는 너무 추상적이에요. 도메인 전문가들은 객체(또는 다른 기술적 용어)가 무엇인지 이해하지 못해요.
이는 결국 팀에 두 가지 언어가 존재해야 한다는 주장입니다. 물론 도메인 모델에는 도메인 전문가와는 관련이 없는 기술적 구성 요소도 포함됩니다. 예를 들면 최적 매입 서비스가 어떤 알고리즘을 통해 최적 매입 방법을 계산하는지는 도메인 전문가는 알지 못해도 괜찮습니다. 그러나 도메인 모델의 핵심은 도메인 전문가가 이해할 수 있어야 합니다. 도메인 전문가야말로 도메인에 대해 가장 잘 알고 있는 사람이고, 도메인에 대해 심층적으로 사고할 수 있는 사람들이기 때문입니다. 수준 높은 도메인 전문가도 모델을 이해하지 못한다면, 모델이 무언가 잘못된 것 입니다.

모델의 단일화와 바운디드 컨텍스트

모델의 용어는 언제나, 누구에게든지 동일한 의미여야 하고, 모델 내에는 모순되는 규칙이 존재하지 않아야 합니다. 이렇게 모델의 내적 일관성을 단일화(unification)라고 합니다. 단일화의 반대 개념은 단편화 입니다. 모델이 단편화 되었음을 알 수 있는 징후는 보통 언어를 혼동하여 구사하는 것입니다. 실제로는 동일한 개념에 대해 여러 언어(와 그에 따른 모델 요소) 존재하거나, 한 언어를 구사하는 사람에 따라 다르게 이해하는 것이 그 예시입니다. 이러한 징후가 포착되는 경우 모델에 존재하는 모순을 수정하여 단일화를 유지해야 합니다.
거대한 프로젝트를 아우르는 하나의 모델을 만들고 단일화를 유지하는 것은 현실적으로 불가능합니다. 100명의 인원이 참여하고, 회원, 주문, 배송 등 다양한 기능을 다루는 프로젝트에서 사용되는 엄청나게 많은 언어들을 모두가 동일하게 이해하도록 유지하는 일은 아주 힘들 것이기 때문입니다. 따라서 이러한 프로젝트는 여러 작은 도메인 모델을 사용할 수 밖에 없는데, 각각의 모델을 기반으로 작성한 코드가 섞이면 버그가 발생하고, 팀원들간의 의사소통도 혼란스러워질 것입니다.
큰 프로젝트가 아니더라도, 프로젝트가 우리가 제어할 수 없는 외부 시스템에 의존하는 경우도 있을 것입니다. 예를 들어 우리의 차종 데이터는 외부 시스템의 차종 데이터를 바탕으로하고 있습니다. 우리가 차종에 관한 우리만의 도메인 모델을 설계하더라도, 결국 그 모델을 구성하는 데이터는 외부로부터 공급되므로 외부 시스템의 차종 모델을 완전히 무시할 수는 없습니다. 어쩔 수 없이 우리의 도메인 모델과 외부의 모델이 공존하는 상황이 발생하고, 이는 우리의 모델의 단일화를 깨트릴 수 있습니다. 단적인 예로 외부 시스템은 차종 정보를 제조사-대표모델-모델-제원-등급-스펙으로 구분하고 우리는 제조사-모델-세부모델-등급-트림-스펙으로 구분합니다. 모델과 등급이라는 동일한 언어가 모델에 따라 다른 의미를 가지므로 코드와 의사소통에 혼란을 야기할 수 있습니다.
이를 방지하기 위해서는 각각의 모델이 적용되는 범위, 즉 컨텍스트(맥락)를 정의하고, 그 컨텍스트 안에서 모델의 단일화를 유지해야 합니다. 컨텍스트의 대상은 코드나 DB 스키마와 같은 물리적인 대상 뿐만 아니라 보편 언어와 조직 구성까지 프로젝트와 그 산출물 전부를 포함합니다. 예를 들어 동일한 계정(account)이라는 언어는 금융 컨텍스트에서는 계좌를 의미하고, 회원 컨텍스트에서는 사용자의 계정을 의미할 것입니다. 계정이라는 언어가 계좌를 의미하는 금융 컨텍스트의 범위는 어디까지인지를 명시적으로 정의해야 도메인 모델을 컨텍스트 내에서 일관되게 유지할 수 있습니다. 또한 이러한 컨텍스트를 담당하는 각 조직이 있는 것이 이상적입니다.
바운디드 컨텍스트가 여러개라면, 컨텍스트 간의 경계를 어떻게 유지할지도 굉장히 중요해집니다. 이와 관련된 개념이 컨텍스트 맵인데, 이는 기회가 된다면 다른 글로 다루어 보겠습니다.

이벤트 스토밍

지금까지 도메인에서 주요 개념을 추출하여 도메인 모델을 설계하고, 이를 근반으로 보편 언어를 구축하여 소통하며, 모델이 적용되는 범위인 바운디드 컨텍스트를 명시적으로 정의해야 한다는 것을 알아 보았습니다. 하지만 실제로 도메인에서 주요 개념이 무엇인지 어떻게 알 수 있으며, 모델이 적용되는 범위는 어떻게 정의해야 할까요?
한 가지 분명한 사실은 그 방법이 무엇이든 도메인에 근거하여 결정을 내려야 하며, 이를 위해서는 도메인 전문가와 긴밀하게 협업하여야 한다는 사실입니다. 하지만 이 사실이 여전히 구체적인 방법은 아닙니다.
이벤트 스토밍은 도메인에서 주요 개념을 포착하고 컨텍스트를 나눌 수 있는 워크샵 형태의 방법론입니다. 이에 대해서는 제가 이전에 작성한 이벤트 스토밍, 어떻게 하는 것이고 왜 해야 하나요? 라는 글을 참고하시기 바랍니다.

스타트업에서의 DDD

DDD의 목적과 핵심 개념들에 대해 알아 보았습니다. 이제 스타트업에 DDD를 적용하려고 할 때 나올 수 있는 고민들에 대해 이야기해 보고자 합니다.

SI 업체들을 위한 패턴이 아닌가요?

Q. 도메인에 존재하는 문제를 제품으로 해결하기 위해 도메인 전문가와 협력하여 제품을 개발하는 과정은 SI 업체가 클라이언트의 문제를 개발팀이 제품을 납품하여 해결하는 과정과 비슷해 보입니다. 도메인주도 설계 책에서 드는 예시들도 대부분 SI 업체에서의 경험 같아 보입니다. DDD는 SI 업체들에게 적합한 패턴이 아닌가요?
A. 좋은 제품을 만들고자하는 SI 업체들에게 DDD는 아주 적합한 패턴입니다. 특히나 만들어야하는 제품의 비즈니스 로직이 아주 복잡하여 도메인에 대한 이해가 깊어야 한다면 더더욱 그렇습니다. 그러나 그렇다고해서 DDD가 스타트업에 부적합한 패턴이라고 할 수는 없습니다. 오히려 제품을 만들고 납품하면 끝인 SI 업체들보다, 제품을 끊임없이 유지하고 개선해야하는 스타트업에게 더욱 필요한 패턴이라고 할 수 있습니다.

비즈니스 로직이 아주 복잡한 경우에만 적합한 패턴이 아닌가요?

Q. DDD는 소프트웨어의 복잡성을 다루는 도구라고 소개하고 있습니다. 그렇다면 복잡하지 않은 소프트웨어의 경우에는 DDD가 필요하지 않거나, 오히려 효율을 떨어뜨리지는 않을까요?
A. SMART UI와 같이 DDD보다 훨씬 단순하고 효율적인 패턴들이 존재합니다. 비즈니스 로직이랄게 존재하지 않고 단순히 CRUD만 필요한 시스템이라면 DDD보다는 더 단순한 패턴이 적합할 것입니다. 빠른 개발과 테스트가 필요한 초기 단계의 스타트업에서 그러한 패턴을 선택하는 것도 결코 잘못된 것이 아니며, 오히려 권장할만 하다고 생각합니다.
그러나 한 가지 고민해 볼만한 점은 있습니다. 제품이 시장에서 기능하기 시작하고 비즈니스 가치를 만들어내는 단계에 들어서면 다양한 의사결정자들에게서 수많은 요구사항들이 쏟아지기 시작할 것이라는 점입니다. 제품은 빠른 시간 내에 수많은 요구사항들을 반영해야하고, 제품은 빠르게 복잡해 질 수 밖에 없을 것입니다. 이 단계에서 DDD는 제품의 복잡성을 제어하기 위한 좋은 패턴이며, 이를 위해 초기 단계에서도 간단한 수준의 도메인 모델을 설계하고 다양한 요구사항들을 반영하기 위해 도메인 모델을 키워나가는 것도 좋은 방법일 수 있습니다.

스타트업에서 도메인 전문가는 누구인가요?

스타트업에서는 이미 존재하는 프로세스를 소프트웨어로 만들기보다는 세상에 없었던 제품을 만드는 경우가 많습니다. 고객의 문제를 풀어야하고, 고객이 제품의 사용자가 될 것이기 때문에 고객이 도메인 전문가라고 볼 수도 있지만, 고객이 자신의 문제가 무엇인지 명확하게 정의할 것임을 기대하기는 어렵습니다. 문제의 대상 영역인 도메인이 무엇인지, 어디까지인지 명확하게 정의되지 않고 따라서 누가 도메인 전문가라고 정의하기도 어렵습니다. 이 경우 도메인은 프로젝트 구성원들이 정의한 문제이고 도메인 모델은 프로젝트 구성원들이 개발하는 해결책, 즉 제품이 됩니다. 따라서 프로젝트에 참여하는 구성원 모두의 합이 도메인 전문가라고 할 수 있고, 도메인 전문가와의 긴밀한 협업이 중요한 것처럼, 프로젝트 구성원들 간의 긴밀한 소통을 통해 도메인 모델의 단일화를 유지하는 것이 중요합니다.

마치며

도메인을 이해하는 것의 중요성을 알고, 그 복잡성을 소프트웨어에서 효과적으로 다루는 역량은 개발자에게 있어 아주 중요한 역량입니다. 오토피디아는 이러한 역량을 가지고 차량 정비 시장을 혁신할 개발자 분들을 찾고 있습니다. 시장의 혁신을 위해 치열하게 고민하는 오토피디아가 궁금하시다면 공식 채용 페이지를 통해 다양한 정보를 확인해보세요.

References

 

오토피디아 채용에 관한 모든 것을 준비했어요

첨단기술을 통한 모빌리티 혁신, 함께 하고 싶다면?

채용 둘러보기

글쓴이

김승수
김승수

백엔드 개발자 | 오토피디아 공동창업자

    0 comments