서두에 결론을 담아서

DDD는 단순히 어플리케이션을 프로그램 코드로 작성하는 것만을 다루지 않는다. DDD의 내용들을 살펴보면 분명 어플리케이션 작성에 대한 내용을 담고 있다. 하지만 다시 한번 잘 살펴보면 이 부분은 프로젝트를 운영하는 방법에 대하여 이야기하는 부분이 더 크다는 것을 알 수 있다. 때문에 DDD를 시작하고자 한다면 내가 속한 조직 내에서 어떠한 부분들에 대해 적용할 지에 대해 진지한 고민이 필요할 것이다. 개인 프로젝트라면 어플리케이션 내의 수많은 조각들을 만드는 부분에서 시작할 수 있지만, 팀 단위 혹은 조직이 커진다면 진지하게 어디서부터 어떻게 적용할지에 대해 고려하고 구성원들과 많은 소통을 통해 나아가고자 하는 방향을 설득해야 할 수 있다.

소프트웨어 엔지니어의 역할

DDD를 먼저 들어가기전에 우리는 소프트웨어 엔지니어에 대해 잠시 고민해봐야 한다. 소프트웨어 엔지니어는 무엇을 하는 사람일까? IT프로젝트 진행을 위해 무언가 프로그램을 만드는 사람들이라고 할 수 있겠다. 그럼 이걸 어떻게 만들어야 할까?
처음 우리가 소프트웨어 엔지니어로 일을 시작하게 되면, 프로그램을 다루게 된다. 내용은 프로젝트 진행자가 요청한 내용을 처리할 수 있는 프로그램 을 작성하는 것 에서부터 시작한다. 그리고 시간이 지남에 따라 영역은 점점 넓어진다. 다른 영역에 눈을 돌릴 수 있게 될 때 부터는 요구사항에 맞춰서 질문도 하고, 좋은 아이디어가 있다면 제안도 할 수 있을 것이다. 그리고 프로그램이 잘 돌아가는지, 그리고 견고하게 만들었는지 테스트고 해야하며 서비스 인프라를 만들어 서비스도 해야 한다.
컴퓨터 프로그램 구현에서 시작해서 기획, 품질관리, 설치 및 배포 등 다양한 영역으로 작업 영역이 늘어나게 된다.

소프트웨어 엔지니어는 프로젝트에 참여해서 프로젝트의 목표에 맞는 프로그램을 만들어야 하는 사람이다.

Domain Driven Design

요구사항을 잘 반영하는 domain model을 정의하는 일에 대한 중요성은 아무리 강조해도 모자람이 없다. 이는 프로젝트 결과물을 결정짓고 나아가 프로젝트가 목표로 하는 방향성에도 영향을 주기 때문이다. 이는 domain model이 프로젝트 혹은 어플리케이션을 구성하는 내용 그 자체이기 때문이다.
Domain model을 잘 만들지 못했다면 비즈니스상의 요구사항을 잘 이해하지 못함에 의한 문제들이 발생한다. 프로젝트가 다른 방향으로 전개되거나, 예정보다 늦게까지 개발되거나, 필요 이상의 개발 시간이 필요해 지기도 한다. 또한 실제 개발내용과 전혀 다른 내용이 개발될 수도 있고, 개발팀 간의 이해 정도에서도 차이가 발생하기에 소통도 시간이 오래걸리고 문제가 발생한다. 그리고 이러한 문제를 수정하는데 시간을 들일 수도 있지만, 프로젝트의 방향성 자체가 틀어질 수도 있다.
DDD에서 tactical design tools 혹은 strategic design tools을 제안하는 이유도 이 domain model에 요구사항을 정확하게 반영하고 그 내용을 모두가 쉽게 이해하고 공유할 수 있도록 하기 위함이다.

Goal

우리가 프로젝트를 진행할 때 가장 먼저 해야하는 것은 당연히 프로젝트에 대한 이해일 것이다. 어플리케이션을 구성하는 모듈들은 각자의 목적에 맞게 설계되어 있을 것이고 이 모듈들은 design pattern에 따라 잘 추상화되어 있을 것이다. 이것을 읽는 소프트웨어 엔지니어들은 프로젝트에 대한 이해와 프로그램의 구성에 대한 지식을 기반으로 프로젝트를 이해하고 개선하고 관리해나갈 것이다. 요구사항을 최대한 명확하게 만들어 코드로 작성하고, 이에 대한 내용을 문서에 작성된 용어를 사용하여 커뮤니케이션 시에 사용할 것이다. DDD는 이를 돕기 위한 수단이다.

그럼 DDD를 할 때 소프트웨어 엔지니어가 가져가야 할 자세는 무엇일까?

소프트웨어 엔지니어는 프로젝트의 목표를 코드화할 수 있고, 코드화된 프로젝트를 읽고 이해할 수 있어야 한다.
우리가 도메인을 이해하고 이를 코드화하기 위해서는 최대한 자세히, 그리고 세밀하게 알아야 한다. 이를 위해 소프트웨어 엔지니어는 도메인의 단위를 가장 작은 것 부터 정의할 수 있도록 요구사항을 잘 분석하고 나누고 정리할 수 있어야 한다. 여기까지 진행하면 도메인에 대한 영향 범위, 관계성 등을 이해하고 도식화할 수 있다. 이 과정을 통해 우리는 프로젝트를 구성하는 어플리케이션의 구성 요소, 작동 방식 등을 설계하고 구현할 수 있게 된다.
요약하자면,

  • Core Domain에 집중하고 이해하고 설계할 것
  • 커뮤니케이션을 할 때에는 Ubiquitous Language를 사용할 것
  • Domain model 설계를 위해 Domain Expert(기획, PO 등)의 지속적인 참여를 유도하고, 엔지니어는 이를 지속적으로 표현 및 교차검증할 것
  • 개발자가 개발/문제해결을 할 때 Domain에 입각해서 생각할 것

그럼 DDD를 어떻게 프로젝트 운영에 접목시킬 수 있을까?

당연하게도 DDD의 가장 첫 시작은 비즈니스, 즉 프로젝트에 대한 이해이다. 비즈니스를 잘 아는 것 만큼 DDD를 성공적으로 운영시킬 수 있는 방법은 없다고 할 수 있겠다. 비즈니스를 구성하는 Domain을 구성하기 위해서는 당연히 비즈니스를 잘 알고 있어야 하기 때문이다. 그리고 domain을 잘 정의하여야 domain model들에 대한 형태와 관계, 작동 방식에 대해 이해할 수 있게 되며, 이것을 기반으로 프로젝트를 어떻게 구성해야 하는지 볼 수가 있다. 반대로, 비즈니스에 대한 전체적인 그림을 확인하고 그에 대한 흐름을 파악한 후, 세세한 domain을 정의하고 그에 대한 유기적인 비즈니스로직을 확인 및 보완하는 방법으로 다가갈 수도 있다. 중요한건 두 가지 방법 모두 비즈니스에 대한 이해가 기본이 되어야 한다는 것이다.
그리고 비즈니스는 상황에 따라서 지속적으로 변화한다. 아키텍트가 이미 모든 내용을 디자인한 후에도 우리는 이 domain model이 지속적으로 변화하는 것을 매우 자주 경험한다. Domain model이 잘 작성되어 있고 그만큼 추상화가 잘 되어 있다면 비교적 적은 공수로도 충분히 리펙토링이 가능하게 된다. 아키텍트가 domain model과 BoundedContext를 최대한 정확하게 디자인하였다면 서비스들이 각자 필요한 정도로 파티셔닝되어 있을 것이고 이는 boundedContext의 경계와 domain model의 관계가 매우 명확하게 정의되었음을 의미한다. 그리고 이는 domain model이 이해하기 쉽고 그만큼 쉽게 분할/병합되기 좋은 형태, 즉 변화에 대응하기 매우 유연한 상태가 되어 있음을 말한다.
이를 통해 우리는,

  • 비즈니스를 보다 명확하게 인지하고 코드화할 수 있다.
  • 코드가 아닌 프로젝트 구성원간의 상호 커뮤니케이션이 보다 수월하고 오류가 적어진다.
  • 비즈니스의 변화에 보다 쉽게 대응할 수 있는 준비를 할 수 있다.

DDD와 관련된 용어들

Domain & Domain Model

Domain은 유저의 행동 혹은 비즈니스, 혹은 목표 그 자체이다. 목표로 하는 그 무엇이든 domain이 될 수 있다. 수학적 모델, 혹은 단순한 계산, BM모델들도 domain이 될 수 있다. 이 domain의 목적이 반영될 수 있도록 잘 추상화하여 코드로 작성하여 프로그램 혹은 어플리케이션 모델을 만들게 되면 이 코드가 domain model이 된다.

Context (= Principle)

Domain을 정의하는 단어, 문장, 상황. 모델이 크거나 작고, 복잡하거나 단순한 것이 아닌 Domain model 그 자체에 대한 정의를 말하며 model이 전체적으로 일관적이며 논리적이어야 함을 의미한다.

Ubiquitous Language

Domain/Domain Model을 정의하고 적용하기 위해 개발자를 포함한 프로젝트 관계자들 모두가 사용하는 같은 의미를 가지는 용어를 일컫는다. 이 내용에는 애매모호한 내용은 절대라고 할 정도로 포함되어서는 안된다. Domain을 이해하기 어렵거나 Domain의 표현이 어색해지는 용어는 지양해야 한다. 또한 Domain을 디자인하는 데 있어 방해되는 모호한 표현 혹은 Domain과의 불일치를 만드는 표현을 지양해야 한다. 이렇게 구축된 Ubiquitous Language는 도메인을 이해하기 쉽게 만들며, 보다 명확한 디자인을 만들고, 만들어진 디자인에 대해 원활한 소통을 제공하여 불필요한 커뮤니케이션 비용을 줄여준다.

Boundary & Bounded Context & Context Map

boundary는 domain을 다루는 팀 조직 구성, 어플리케이션의 일부(sub system), 물리 장비 혹은 db 스키마 등의 일부로 들어갈 수도 있다.
DDD는 거대한 모델을 나누어 명확한 경계를 만들고, 상호간의 명확한 연관성을 만들어주는 작업이다. 이 경계와 경계를 이루는 domain(하나 혹은 다수의)의 상호작용을 boundary라 부른다. 그리고 이 boundary를 설명할 때에 그것을 BoundedContext라고 한다. boundary를 설명하는 context로서 Domain을 정의할 때 단어, 문장, 상황을 사용해 설명하듯, bounded context는 boundary가 어떤 것인지, 혹은 어떻게 동작하는지 설명할 수 있는 단어, 문장, 상황을 말한다. 그리고 여러 개의 bounded context간의 관계를 묘사한 도식도, 문서 를 Context Map이라고 한다

BoundedContext & ContextMap

BoundedContext & ContextMap

위의 그림에서는 하나의 큰 어플리케이션 모델을 2개의 boundedContext(Sales Context와 Support Context)로 나누고, 각각의 boundary 안에는 다수의 모델이 들어 있다. 그리고 두개의 boundary는 서로의 커뮤니케이션을 위해 Customer/Product model을 가지고 있다.
여기에서는 단지 2개로 BoundedContext를 나누었을 뿐이지만, 사실 이 boundedContext를 나누는 기준은 매우 다양하다. model간의 상호작용, 다루는 팀의 상황 등 어플리케이션의 영역 이외에도 이 내용을 정의하는데 주는 요소는 너무나도 많기 때문에 같은 모델일지어도 상황에 따라 다른 boundedContext를 정의할 수 있으며, 이 때 특별한 이유(타 boundedContext와의 커뮤니케션과 같은)로 boundedContext와 전혀 관계가 없어 보이는 domain model이 포함될 수도 있다. 이는 전혀 이상한 것이 아니다. 우리의 상황을 만드는 것은 어플리케이션이나 프로젝트의 목표가 아닌 우리가 일하는 방향에서 비롯되며, 이는 프로젝트 구성원 간의 커뮤니케이션과 상호작용에서부터 시작함을 기억해야 한다.

Continuous Integration

지금은 프로젝트를 진행할 때 자주 거론되는 그 CI가 맞다. 주기적/지속적으로 코드 혹은 관련된 문서, 요소들을 자동적으로 통합 및 테스트를 진행하여 요소들의 파편화를 방지하려는 수단이다. 더불어 이러한 작업을 통해 당사자들간의 이해의 차이를 최소한으로 좁히려는 노력이 포함된다.

Layered Architecture

DDD는 매우 복잡한 구조를 처리하기 위해 만들어졌다. 그리고 이것을 한번에 처리하기에는 너무나도 버겁기 때문에 이를 위해 4가지 계층적 구조를 제안한다. 프로그램을 몇몇 레이어로 나누어 설계토록 한다. 각각 레이어는 레이어 내부에서만 응집력을 가지도록 하고, 레이어 간에는 느슨하게 관계를 가질 수 있도록 하는 것이 목표이다.
domain model과 business logic을 서로 격리시키고 infrastructure, user interface, application logic을 business logic에서 격리시킨다. 도메인 모델을 하나의 레이어에 격리되도록 하고, user interface, application, infrastructure code와 격리시킨다. Domain model은 application에서 관리될 수는 있지만 저장 혹은 관계에 대해서는 도메인 객체 내부에서 처리하도록 한다. 이 과정을 통해서 도메인을 더 명확하게 만들 수 있고 비즈니스의 목적을 잘 구현할 수 있게 된다.

  • User Interface Layer: 유저의 요청이 처리되고 정보를 표현할 수 있는 부분. Contoller, Listener 등
  • Application Layer: transaction, dto, domain에 대한 처리 및 관리가 이루어지는 부분. Service/Business Logic 등
  • Domain Layer: domain에 대한 상태 및 정보를 담아두는 부분. DTO, Domain-model, Schema 등
  • Infrastructure Layer: 위 3개 레이어를 서포트할 수 있는 부분. Repository, Adapter, Framework, Library 등

Reference

Appendix

  • Design Pattern: 프로그램에 대한 추상화된 설명. 그리고 프로그램을 더 구조화된 형태로 작성할 수 있는 방법론
  • Module: 소프트웨어의 복잡한 구조를 구성할 수 있는 독립적인 혹은 표준적인 단위. 우리 엔지니어들은 이 모듈을 design pattern을 사용해 추상화시키고 설명한다.