Skip to content

Commit

Permalink
doc(draft): 도메인 주도 설계 핸드북
Browse files Browse the repository at this point in the history
  • Loading branch information
siyul-park committed Aug 10, 2024
1 parent aa1c732 commit bca21ef
Showing 1 changed file with 97 additions and 0 deletions.
97 changes: 97 additions & 0 deletions _posts/2024-08-09-domain-driven-design-handbook.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
---
title: "도메인 주도 설계 핸드북"
date: 2024-08-09 12:00:00 +0900
categories: "pattern"
tags: ["pattern", "ddd"]
---

세상에는 변하지 않는 것이 없습니다. 시장은 계속 변하고, 고객의 요구사항도 시시각각 달라집니다. 이러한 불안정한 환경에서 우리는 고객에게 신속하게 가치를 전달해야 합니다. 따라서 소프트웨어는 변화를 수용할 수 있도록 유연해야 합니다. 변화는 기능적 요구사항과 비기능적 요구사항으로 나뉘며, 대부분 비즈니스에서 기인합니다. 복잡성을 줄이고 지속적으로 변화에 대응할 수 있는 소프트웨어를 만들기 위해서는 비즈니스 도메인에 집중해야 합니다.

비즈니스 도메인과 구현 간의 간격을 좁혀 도메인과 코드가 긴밀하게 연동되도록 해야 합니다. 코드는 도메인을 표현하는 한 방법일 뿐이며, 도메인이 변화하면 코드도 자연스럽게 수정되어야 합니다. 도메인과 코드 사이의 간격이 크면 도메인의 작은 수정도 코드에는 큰 변화를 요구하여 가치를 전달하는 데 시간이 더 걸립니다. 반면, 간격을 좁히면 도메인의 수정이 적은 코드 변경으로 반영되어 더 빠르게 대응할 수 있습니다.

도메인과 밀접하게 연결된 코드는 도메인이 정제됨에 따라 관련된 개념은 가까워지고, 차이가 있는 개념은 멀어지며, 의존성은 줄어들고 응집성은 증가하게 됩니다.

도메인 전문가와 개발자들이 긴밀하게 협력하면 고객에게 더 설득력 있는 제품을 제공할 수 있습니다. 다양한 관점에서 비즈니스 도메인을 탐구하고 숨겨진 개념을 표면으로 끌어내어 고객에게 가치를 제공할 수 있습니다. 이를 통해 파편화된 지식을 공유하고 통합함으로써 도메인 전문가와 개발자 간의 단절을 해소하고, 실제적인 비즈니스 가치를 제공하는 소프트웨어를 만들 수 있습니다.

따라서 비즈니스 도메인을 깊이 이해하고 이를 바탕으로 비즈니스 개념을 도메인 모델로 명확히 정의한 후, 이 모델을 기반으로 소프트웨어를 개발해야 합니다.

## 유비쿼터스 언어 (Ubiquitous Language)

우리가 사용하는 언어는 종종 모호하고 모순적입니다. 동일한 단어도 문맥에 따라 다른 의미를 가질 수 있습니다. 비슷한 표현을 사용한다고 생각하지만, 각자가 다른 의미로 해석하여 서로 다른 목표를 향해 나아갈 수 있습니다. 이러한 언어의 차이로 인해 소통이 어려워지면, 소통의 기회가 줄어들고 도메인 탐구나 지식 공유의 기회도 감소하게 됩니다. 그 결과, 제품은 고객의 기대와 멀어질 수 있습니다.

도메인 전문가와 소프트웨어 개발자들이 공통의 심적 모델을 유지하려면, 사용하는 언어를 통일해야 합니다. 다양한 전문가들이 함께 비즈니스 도메인에 대한 지식을 공유하고 심층적으로 탐구하며, 공통 언어인 **유비쿼터스 언어(Ubiquitous Language)**를 정제해야 합니다. 이 과정에서 언어의 모순이나 오류를 찾아내고, 도메인 내의 본질적인 개념을 도출하여 도메인 모델을 명확히 정의할 수 있습니다.

어휘들의 간략한 정의가 포함된 사전을 만들어 유비쿼터스 언어를 체계화할 수 있습니다. 사전에 어휘와 정의를 기록하고, 정의 속에서 새롭게 등장하는 표현을 발굴하여 언어를 정제합니다. 이 사전을 참조하여 유비쿼터스 언어로 사용자 스토리를 작성하고, 사용자 스토리에서 나타난 언어의 변화를 다시 사전에 반영하여 언어를 지속적으로 확장해 나갑니다.

사전과 유비쿼터스 언어를 담은 여러 문서는 지속적으로 확장되고 변화합니다. 이러한 문서를 꾸준히 유지하고 관리하는 것은 쉬운 일이 아닙니다. 코드는 가장 지속적으로 유지되고 유일하게 보장되는 유비쿼터스 언어의 표현이지만, 코드 자체는 소프트웨어 개발자가 아닌 다른 구성원들이 접근하기 어렵습니다. 따라서 모든 구성원이 쉽게 접근할 수 있는 방식으로 정보가 전달되어야 합니다. 이러한 문서는 코드를 기반으로 자동으로 생성되고 관리될 수 있지만, 소프트웨어 개발자가 아닌 다른 구성원들도 쉽게 이해할 수 있어야 합니다.

유비쿼터스 언어는 코드, 구성원들이 소통에 사용하는 문장들, 그리고 유비쿼터스 언어로 작성된 문서들로 구성됩니다. 유비쿼터스 언어로 작성된 구문은 그 언어의 발현이자, 언어 자체를 나타냅니다.

유비쿼터스 언어에 포함된 명사, 형용사, 동사, 그리고 풍부한 표현들은 코드에 통합되고 명시적으로 반영되어야 합니다. 유비쿼터스 언어는 특정 비즈니스 도메인의 개념을 포착하기 위해 사용되며, 코드와 테스트는 이 언어를 기준으로 작성되어야 합니다.

유비쿼터스 언어를 도출하는 과정에서 동일한 어휘가 서로 다른 개념을 나타낼 수 있습니다. 이는 어휘가 다양한 문맥에서 사용되기 때문입니다. 이러한 미세한 차이를 정제하여 하나의 일관된 개념으로 통합하거나, 두 개의 개념 차이를 인정하고 동일한 문맥에서 다른 어휘를 사용하거나, 문맥의 차이를 수용하여 두 개의 유비쿼터스 언어를 사용할지를 결정해야 합니다.

이렇게 나누어진 문맥과 언어의 경계를 **바운디드 컨텍스트(Bounded Context)**라고 합니다. 하나의 바운디드 컨텍스트는 하나의 유비쿼터스 언어를 가집니다. 유비쿼터스 언어는 해당 언어가 속해 있는 바운디드 컨텍스트 안에서만 유효합니다.

## 바운디드 컨텍스트 (Bounded Context)

**바운디드 컨텍스트(Bounded Context)**는 유비쿼터스 언어를 표현하는 도메인 모델을 담아내는 명확한 경계입니다. 이 경계 안에서 유비쿼터스 언어의 어휘와 구문은 구체적인 의미를 가지며, 정확성을 보장하고 도메인 모델을 표현합니다. 각 바운디드 컨텍스트는 서로 다른 유비쿼터스 언어를 사용하며, 서로 격리됩니다. 일부 용어가 다른 바운디드 컨텍스트와 겹치더라도 각기 다른 의미를 지닙니다. 복잡한 개념은 여러 바운디드 컨텍스트로 나뉘어 구체적이고 풍부한 의미를 드러냅니다.

도메인 모델은 바운디드 컨텍스트를 구성하는 핵심 요소이지만, 바운디드 컨텍스트 내부에는 도메인 모델과 상호작용을 지원하는 다른 요소들도 포함됩니다. 도메인 모델을 영구적으로 저장하기 위해 데이터베이스 스키마가 바운디드 컨텍스트의 유비쿼터스 언어로 표현된다면, 그 스키마는 바운디드 컨텍스트 내부에 존재합니다. 반면, 스키마가 유비쿼터스 언어로 표현되지 않고 별도로 관리된다면, 바운디드 컨텍스트 외부에 위치하게 됩니다.

바운디드 컨텍스트는 독립적인 서비스나 어플리케이션을 나타낼 수도 있습니다. 별도로 분리된 외부의 서비스는 독립된 문맥을 가지고 있으며 별도의 언어를 사용합니다.

바운디드 컨텍스트를 도출할 때는 적절한 경계를 설정해야 합니다. 바운디드 컨텍스트가 너무 엄격하면 필수적인 개념이 누락되어 풍부한 표현력을 잃고 점차 무기력해질 것입니다. 반대로, 바운디드 컨텍스트가 너무 포괄적이면 하나의 도메인 모델이 상충되고 모순되는 개념을 담게 되어, 개념을 충실하고 구체적으로 표현하지 못하고 범위를 넘어 흘러넘치게 됩니다.

프로젝트의 모든 것을 포괄하는 언어를 만드는 것은 거의 불가능하며 유용하지도 않습니다. 순수하고 명확하게 모든 개념을 정의하고 모든 구성원의 동의를 받는 것은 매우 어렵습니다. 조직이 작더라도 오랫동안 유지되는 유일하고 일관된 개념을 만드는 것은 여전히 어려운 일입니다. 따라서 하나의 어휘가 여러 의미를 내포할 수 있다는 점을 받아들이고, 바운디드 컨텍스트를 통해 차이점을 명확하게 표현하며, 이 차이점을 잘 이해하는 여러 도메인 모델을 각각 기술해 나가야 합니다.

바운디드 컨텍스트는 서로 간의 의존성을 최소화합니다. 동일한 어휘로 표현되는 도메인 개념이라도 각 바운디드 컨텍스트는 별개의 언어를 내포하고, 이를 서로 다른 의미로 해석하여 모델링합니다. 다른 바운디드 컨텍스트에서 정의된 모델을 활용하려면, 이를 해당 컨텍스트의 언어로 변환하는 과정이 필요합니다. 동일한 모델이 여러 관점에서 다뤄질 수 있어 중복이 발생할 수 있지만, 바운디드 컨텍스트는 서로 강하게 연결되지 않고 각자의 목적에 맞게 설계될 수 있습니다.

## 도메인 (Domain)

비즈니스는 여러 도메인으로 구성됩니다. 이러한 서브 도메인은 제품을 경쟁자와 차별화하는 **핵심 도메인 (Core Domain)**, 비즈니스와 밀접하게 연관되어 있지만 경쟁 우위를 가질 필요가 없는 **지원 서브 도메인 (Supporting Sub-Domain)**, 그리고 비즈니스와 직접적인 연관은 없지만 정상적인 운영을 위해 필요한 **범용 서브 도메인 (Generic Sub-Domain)**으로 구분됩니다.

같은 도메인 영역이라도 비즈니스에 따라 핵심 도메인으로 분류될 수 있습니다. 핵심 도메인은 경쟁자들과의 차별화를 통해 경쟁 우위를 제공하며, 비즈니스 수익을 창출하는 중요한 요소입니다. 따라서 경쟁자가 쉽게 모방하지 못하도록 진입장벽을 높이는 것이 중요합니다. 반면, 지원 서브 도메인은 핵심 도메인처럼 차별화될 필요는 없지만, 핵심 도메인과 밀접하게 연결된 영역으로서 기성 제품만으로는 해결할 수 없는 문제를 다루는 경우가 많습니다. 범용 서브 도메인은 차별화가 필요하지 않으므로 기존 제품을 활용할 수 있습니다. 자원이 한정된 상황에서는 비즈니스 성공에 직접적인 영향을 미치는 핵심 도메인을 명확히 식별하고, 여기에 역량을 집중해야 합니다.

비즈니스는 **문제점 공간****해결책 공간**으로 나눌 수 있습니다. 문제점 공간은 해결해야 할 비즈니스 문제에 초점을 맞추고, 해결책 공간은 이러한 문제를 해결하는 소프트웨어 구현에 집중합니다.

문제점 공간은 핵심 도메인과 그에 연관된 서브 도메인들로 구성됩니다. 명확하게 정의된 문제점 공간을 통해 비즈니스에 중요한 영향을 미치는 핵심 도메인과 이를 지원하는 서브 도메인들을 식별하고, 모든 구성원이 공감할 수 있는 비전과 목표를 수립해야 합니다.

해결책 공간은 하나 이상의 바운디드 컨텍스트로 구성된 구체적인 소프트웨어 모델의 집합입니다. 바운디드 컨텍스트는 문제를 해결하기 위한 특화된 해결책이자 소프트웨어 구현의 이정표가 되어 문제를 소프트웨어로 구현하는 데 활용됩니다. 해결책 공간은 새로 만들어질 시스템과 기술뿐만 아니라 기존에 존재하는 시스템과 기술에도 영향을 받습니다.

서브 도메인을 바운디드 컨텍스트와 일치시키는 것은 문제점 공간과 해결책 공간을 효과적으로 통합하고, 도메인 모델을 비즈니스 목표에 맞게 명확히 구분하는 데 유리합니다. 그러나 레거시 시스템을 고려하면, 서브 도메인이 여러 바운디드 컨텍스트와 중첩될 수 있습니다. 예를 들어, 하나의 바운디드 컨텍스트가 여러 서브 도메인으로 구성되거나, 반대로 하나의 서브 도메인이 여러 바운디드 컨텍스트로 나뉘는 경우가 있을 수 있습니다.

서브 도메인과 바운디드 컨텍스트를 명확히 정의하고, 이들의 연관성을 정확히 파악해야 합니다. 서브 도메인과 바운디드 컨텍스트가 항상 일대일로 대응하지 않을 수 있지만, 만약 하나의 서브 도메인이 여러 바운디드 컨텍스트에 걸쳐 있거나, 하나의 바운디드 컨텍스트가 지나치게 많은 서브 도메인과 연결되어 있다면 구분이 적절히 이루어지지 않았다는 신호일 수 있습니다.

## 컨텍스트 맵 (Context Map)

바운디드 컨텍스트 간의 경계와 관계가 명확히 표현된 **컨텍스트 맵 (Context Map)**를 통해 해결책 공간을 쉽게 이해할 수 있습니다. 이를 통해 전체 시스템 구조를 파악하고, 바운디드 컨텍스트 간의 의존성을 관리하며, 통합 방식을 결정할 수 있습니다.

각 바운디드 컨텍스트와 이들 간의 관계를 기록하여 컨텍스트 맵을 작성합니다. 바운디드 컨텍스트의 경계, 컨텍스트와 팀 간의 관계, 서로 다른 컨텍스트 간의 통합 방식, 그리고 모델 변환 방식을 높은 수준에서 표현하여 현재 프로젝트의 상태를 간단한 다이어그램으로 나타냅니다.

세부 사항이 너무 많아지면 컨텍스트 맵이 복잡해져 직관적인 이해가 어려워지고, 수정 및 관리가 힘들어질 수 있습니다. 따라서 컨텍스트 맵은 최대한 단순하게 유지하며, 변화하는 상황에 맞춰 유연하게 업데이트해야 합니다.

현재 시스템 구조를 정확히 반영하게 컨텍스트 맵을 표현해야 합니다. 이상적인 모습을 그리기보다는 현재 상태를 명확히 이해할 수 있도록 매핑하고, 프로젝트가 진행됨에 따라 관계가 바뀌면 맵을 업데이트하여 최신 상태를 유지해야 합니다.

바운디드 컨텍스트는 여러 가지 정해진 관계를 가질 수 있습니다. 예를 들어, 컨텍스트 내 팀이 성공과 실패를 함께한다면, 이 관계를 **파트너십**이라고 합니다. 이러한 팀들은 개발과 통합을 공동으로 관리하기 위해 잘 조정된 기획 과정을 갖추고, 양측의 요구를 수용할 수 있는 인터페이스를 설계하며 긴밀하게 협력해야 합니다.

또한, 도메인 모델에서 공유되는 코드는 밀접한 상호 의존성을 형성할 수 있습니다. 팀이 공유하기로 합의한 도메인 모델의 일부는 **공유 커널**로 지정할 수 있으며, 이 공유 커널은 다른 팀의 동의 없이 변경할 수 없고, 영향도가 크기 때문에 신중하게 관리되어야 합니다.

다운스트림 팀의 요구사항을 다양한 방법으로 수용해야 하는 관계를 **고객-공급자** 관계라고 합니다. 이 경우, 업스트림 팀의 계획이 다운스트림 팀의 계획에 강한 영향을 미치므로, 업스트림 팀은 다운스트림 팀의 요구사항을 반영해야 합니다.

그러나 업스트림 팀이 다운스트림 팀의 요구사항을 수용할 동기가 없는 경우, 다운스트림 팀은 업스트림 팀의 모델을 무조건적으로 준수해야 합니다. 이러한 관계를 **순응주의자**라고 하며, 다운스트림 팀은 업스트림 팀의 모델을 그대로 따름으로써 컨텍스트 사이에 발생하는 복잡한 변환을 제거합니다.

잘 설계된 바운디드 컨텍스트의 경계를 연결할 때 변환 계층은 간결합니다. 하지만 바운디드 컨텍스트 간의 거리가 멀어질수록 변환은 더 복잡해지고 방어적인 성격을 가지게 됩니다. 이러한 변환 계층을 **부패 방지 계층**이라고 하며, 다운스트림에서 분리된 변환 계층을 통해 업스트림의 모델을 다운스트림의 맥락에 맞는 모델로 조정합니다.

서비스에 접근할 수 있는 프로토콜이 명확히 정의되어 공개된 서비스를 **오픈 호스트 서비스**라고 합니다. 이 서비스는 공개된 프로토콜을 통해 상호작용하며, 새로운 요구사항에 대응하기 위해 프로토콜을 확장하고 조정할 수 있어야 합니다.

두 바운디드 컨텍스트 간의 상호작용을 위해 잘 문서화된 공유 언어를 사용할 수 있습니다. 이 언어를 **발행된 언어**라고 하며, 이를 참고하여 바운디드 컨텍스트 간의 변환을 처리하게 됩니다. 발행된 언어는 오픈 호스트 서비스와 함께 종종 사용됩니다.

만약 두 바운디드 컨텍스트 사이에 유의미한 관계가 없다면, 이들은 서로 관계가 없음을 명확히 선언해야 합니다. 이러한 관계를 **분리된 방법**이라고 하며 작은 범위 내에서 단순하고 전문적인 솔루션을 찾을 수 있게 합니다.

이렇게 바운디드 컨텍스트 간의 관계가 잘 표현되게 작성된 컨텍스트 맵은 높은 수준의 관점을 제공하여 아키텍처 결정에 활용되거나, 조직 역학을 이해하고 프로젝트 진행의 장애 요소를 파악하는 데 도움을 줍니다.

## 참고 문서

- [도메인 주도 설계 구현, 반 버논](https://product.kyobobook.co.kr/detail/S000000935852)
- [도메인 주도 설계, 에릭 에반슨](https://product.kyobobook.co.kr/detail/S000001514402)

0 comments on commit bca21ef

Please sign in to comment.