본문 바로가기

더 나은 엔지니어가 되기 위해/읽고 쓰며 정리하기

도메인 주도 설계 핵심(반 버논, 2017)

도메인 주도 설계 핵심 정리

들어가며

이 글은 "반 버논님의 도메인 주도 설계 핵심 (2017)" 책을 읽고 회고 겸 정리한 글이다.

도메인 주도 설계 핵심

내게 임팩트 있던 내용들만 빠르게 훑어보며 정리해본다.


1장. 나에게 도메인 주도 설계는

이 장에서는 DDD의 중요성과 이 책의 개략적인 내용에 대해 설명한다.
이 책을 통해 우리는 크게 3가지에 대해 배우게 된다.

  • 전략적 설계 (What)
    • 전략적 설계는 세세한 구현으로 들어가기 앞서, "무엇을 할지"에 대해 큰 그림을 그리는 일이다.
    • 바운디드 컨텍스트, 보편 언어, 서브 도메인, 컨텍스트 매핑 등에 대해 다룬다.
  • 전술적 설계 (How)
    • 전략적 설계 이후, 세세하게 "어떻게"에 대해 다루는 일이다.
    • 엔티티, 값 객체, 애그리게이트, 도메인 이벤트 등에 대해 다룬다.
  • 학습 과정과 지식의 경계
    • DDD는 도메인과 비즈니스 지식을 더욱 발전시킬 수 있도록 팀에 생각하는 법을 알려준다.
    • 어떻게 이를 실현할 수 있는지 도구들을 배운다.

2장. 바운디드 컨텍스트 및 보편언어와 전략적 설계

  • 바운디드 컨텍스트 (Bounded Context) 란?
    • 바운디드 컨텍스트는 큰 도메인 덩어리를 여러 문맥에 맞게 나눈 덩어리이다.
    • 나누지 않고, 계속해서 하나의 덩어리 추가해나가면 너무 많은 사람들이 이 덩어리에 참여하게 된다.
    • 나중에는 이해하기에도, 추가/변경하기도 어렵다. 이런 상태를 큰 진흙 덩어리라고 한다.
    • 바운디드 컨텍스트는 의미적으로 동일한 컨텍스트의 범위를 표현하고 나눈 것이다.
      • 도메인 전문가들과 이야기하며, 컨텍스트를 나눠야한다.
    • 바운디드 컨텍스트 내 컴포넌트들은 컨텍스트에 특화되어 있으며, 이 안에서 의미가 살아난다.
    • 바운디드 컨텍스트는 컨텍스트에 맞는 보편 언어를 가진다.
      • 팀의 누군가가 보편언어 표현을 사용하면 팀 모두가 그 표현이 가진 제약사항과 정확한 의미를 이해한다.
    • 바운디드 컨텍스가 조직의 핵심 전략적 계획으로 개발되고 있을 때 이를 핵심 도메인이라고 한다.
      • 기업의 올바른 전략적 결정을 위해 무엇이 핵심 도메인이어야 하고, 어떤 것을 제외시켜야 하는지 현명하게 선택해야 한다.
    • 각각의 바운디드 컨텍스트는 단일 팀에만 할당되어야 한다.
    • 각각의 바운디드 컨텍스트는 독립적인 소스 코드 리파지토리가 있어야 한다.
    • 각각의 바운디드 컨텍스트마다 데이터베이스 스키마도 명확히 분리한다.
  • 기본적인 전략적 설계를 하려면
    • 바운디드 컨텍스트를 사용한다는 것은 "핵심이 무엇인가?"라는 질문에 답하도록 유도한다.
    • 바운디드 컨텍스트는 핵심이 되는 모든 개념들은 포용하고, 나머지는 모두 제외시켜야 한다.
      • 제외할 내용들은 엄격하게 걸러내야 한다. 그리고 살아남은 개념들은 해당 컨텍스트 내 보편언어의 일부가 된다.
    • 핵심이 무엇인지는 도메인 전문가와 협업하며 찾아야 한다.
      • 도메인 전문가와 소프트웨어 개발자를 하나의 협업 팀으로 만들어야 한다.
      • 팀의 보편 언어의 토대는 도메인 전문가들의 멘탈 모델이다.

3장. 서브도메인과 전략적 설계

  • 전체 도메인을 여러 부분 도메인으로 나눈 것을 서브 도메인이라고 한다.
  • 하나의 서브 도메인은 하나의 바운디드 컨텍스트를 가지는 것이 이상적이다.
  • 프로젝트에는 세 가지 주요 서브도메인 유형이 있다.
    • 핵심 도메인
      • 신중하게 만들기 위한 전략적 투자 영역이다.
      • 이 도메인은 다른 경쟁자들에 대한 차별화를 만들 영역이기 때문에 기업 차원에서 높은 우선순위를 가진다.
      • 핵심 도메인은 기업이 뛰어나야 하는 부분에 대한 경계를 구분해준다.
    • 지원 서브 도메인
      • 핵심 도메인 만큼은 차별화를 가져야 되는 영역은 아니지만, 프로젝트를 성공하기 위해 필요한 도메인이다.
      • 핵심 도메인과 마찬가지로 이미 존재하는 제품으로 해결할 수 없는 맞춤 제작 개발이 필요하다.
    • 일반 서브 도메인
      • 기존 제품 구매를 통해 바로 충족시킬 수 있는 경우에 해당하는 도메인이다.
      • 제일 투자 영역이 적은 도메인이다.
  • 하나의 바운디드 컨텍스트는 오직 1개의 서브 도메인 모델을 두는 것을 목표로 한다.
    • 이게 불가능하다면, 핵심 도메인으로부터 완전히 분리된 모듈 형태로라도 도메인 모델을 분리해야 한다.

4장. 컨텍스트 매핑과 전략적 설계

  • 바운디드 컨텍스트간 연결되어 있는 개념을 통합하는 일을 컨텍스트 매핑이라고 한다.
    • 이런 매핑은 컨텍스트 맵에서 선으로 표시한다.
    • 각 컨텍스트 간 각각의 보편언어가 있는 것을 생각해보면, 이 선은 두 언어 사이의 통역을 나타낸다고 볼 수 있다.
    • 팀에 대한 것이든 기술적인 것이든 모두 관계선으로 표시할 수 있다.
  • 매핑의 종류는 다음과 같은 것들이 있다.
    • 파트너십
      • 두 팀이 함께 성공하거나 다같이 실패한다는 관계다.
      • 각 팀은 하나의 바운디드 컨텍스트를 책임진다.
    • 공유 커널
      • 팀 사이에 작지만 공통인 모델을 공유하는 관계다.
      • 공유 모델에 대해 지속적인 합의와 관리가 필요하다.
    • 고객-공급자
      • 공급 역할을 하는 상류 팀(Upstream)과 이에 대해 고객 역할을 하는 하류 팀(Downstream) 관계다.
      • 관계를 주도하는 것은 공급자다.
    • 준수자
      • 상류 - 하류 팀 관계에서, 상류 팀이 하류 팀의 특정 요구에 지원할 동기가 없는 경우에 나타난다.
      • 예를 들면, 아마존과 제휴하는 판매자 중 하나가 아마존 시스템과 통합하려고 할 때 아마존 모델을 준수한다.
    • 반부패 계층
      • 상류 - 하류 팀 관계에서, 하류 팀이 그들의 보편언어 모델과 상류 팀의 보편언어 모델 사이에 번역 계층을 만드는 것이다.
      • 서로 다른 언어로 말하는 두 팀 사이를 번역하는 번역가를 고용한 것에 가깝다.
      • 있으면 좋지만 여러가지 형태로 비용이 높다.
    • 공개 호스트 서비스
      • 바운디드 컨텍스트에에 대한 접근을 제공하는 프로토콜이나 인터페이스를 정의한 계층이다.
      • 예를 들면 웹 API 등이 여기에 해당한다.
    • 공표된 언어
      • 공개 호스트 서비스는 서드파티에게 최상의 통합 경험을 줄 수 있는 공표된 언어를 제공한다.
      • XML, JSON 스키마나 Protobuf 같은 것들로 작성할 수 있다.
  • 컨텍스트 매핑으로 각 바운디드 컨텍스트를 통합할 때 데이터베이스 통합은 피해야한다.
  • 컨텍스트 매핑 방법은 다음과 같은 것들이 있다.
    • RPC (Remote Procedure Call)
      • 아마 속도는 가장 빠르긴 하나(gRPC 기준) 컨텍스트 사이에 가장 강한 결합이다.
    • RESTful HTTP
      • 리소스(URI)는 유스케이스의 외적 표현이다.
      • 흔한 실수는 도메인 모델 안에 직접적으로 애그리게이트를 반영하는 리소스를 설계하는 것이다.
      • 리소스가 클라리언트 주도의 유스케이스를 지원할 수 있도록 종합적으로 설계해야 한다.
        • 도메인 모델 그자체가 아니라, 클라이언트가 원하는 것을!
    • Message
      • 컨텍스트 사이에 가장 약한 결합이다.
      • 즉각적인 결과가 필수적이지 않을 때, 메시징을 사용해 좀 더 견고하게 시스템을 구축할 수 있다.
      • 소비자가 사용하는 도메인 이벤트는 클래스같은 코드가 아니라, 이벤트의 스키마, 즉 공표된 언어에만 의존해야 한다.
      • 수신자는 멱등 수신자(여러번 같은 오퍼레이션이 수행되도, 동일한 결과를 내도록 동작)여야 한다.
      • 메시징 메커니즘은 항상 비동기 요청-응답 통신을 사용하기 때문에 어느정도 지연이 일반적이고 타당하다.

5장. 애그리게잇과 전술적 설계

  • 바운디드 컨텍스트 안에는 애그리게잇이 존재한다.
    • 애그리게잇는 1개 이상의 엔티티와 값 객체로 구성되고, 그 중 하나의 엔티티는 애그리게잇 루트라고 부른다.
    • 애그리게잇 루트는 애그리게잇 안의 다른 모든 요소를 소유한다.
    • 각 애그리게잇은 일관성있는 트랜잭션 경계를 형성한다.
    • 하나의 트랜잭션은 오직 1개의 애그리게잇만을 수정하고 커밋해야한다.
  • 애그리게잇 설계 규칙
    • 애그리게잇 경계 내에서 비즈니스 불변사항을 보호하라.
      • 트랜잭션이 커밋될 때 비즈니스의 일관성이 지켜지는 것에 기반을 두고, 애그리게잇 구성 요소를 결정해야 한다는 의미다.
    • 작은 애그리게잇을 설계하라.
      • 각 애그리게잇의 메모리 사용량과 트랜잭션 범위가 비교적 작아야 함을 강조한다.
      • 애그리게잇이 작으면, 빠르게 로드되고, 더 작은 메모리를 차지하고, 더 자주 성공적인 트랜잭션을 수행할 것이다.
      • 개발자의 관리 범위도 작아지고, 테스트도 쉬워진다.
      • SRP를 떠올려라. 애그리게잇이 너무 많은 일을 하고있지 않은가?
    • 오직 ID를 통해 다른 애그리게잇을 참고하라.
      • 애그리게잇을 작게 유지하고, 동일한 트랜잭션 내 여러 애그리게잇을 수정하려는 접근을 방지해준다.
    • 결과적 일관성을 사용해 다른 애그리게잇을 갱신하라.
      • 애그리게잇의 트랜잭션 일부로 도메인 이벤트를 발행시켜라.
      • 다른 애그리게이트에서 이 도메인 이벤트를 구독하여 애그리게잇을 갱신한다.
      • 도메인 이벤트를 구독하는 애그리게이트는 같은 바운디드 컨텍스트일 수도 있고, 다른 바운디드 컨텍스트일 수도 있다.
    • 위 설계 규칙은 엄격하게 지키라고 강요하기보다는, 도움을 줄 수 있는 가이드라고 받아들이자.
  • 애그리게잇 모델링
    • getter/setter만 가진 빈약한 도메인 모델은 피하자.
    • 도메인 모델에는 비즈니스 로직이 있어야 한다. 애플리케이션 서비스에 비즈니스 로직이 새어나가지 않도록 한다.
    • 애그리게잇의 모든 부분은 보편언어에 따라 모델링해야 한다.
      • 만드는데 급급해서는 안된다. 모든 것이 조화를 이룰 수 있도록 도메인 전문가와 개발자들 사이의 긴밀한 협업이 필요하다.
    • 추상화를 조심스럽게 선택하라.
      • 추상화는 유연해지는 대신 복잡도를 높인다.
      • 소프트웨어의 모델 언어가 도메인 전문가의 멘탈 모델과 일치하지 않을 수 있다.
    • 올바른 크기의 애그리게잇을 설계하는 방법
      • 초기에는 먼저 작은 애그리게잇을 설계하는데에 집중한다.
        • 오직 1개의 루트 엔티티를 갖는 애그리게잇을 생성한다.
        • 애그리게잇을 초기에 만들 때 필요한 상태들을 루트 엔티티 속성으로 정의한다.
      • 이미 정의한 다른 애그리게잇 중에 애그리게잇이 변경될 때 함께 갱신되어야할 애그리게잇이 있는지 확인한다.
      • 갱신이 일어나는데 걸리는 시간이 얼마나 허용되는지 도메인 전문가에게 확인한다.
        • 즉시 갱신되어야할 경우, 동일한 애그리게잇 안에 2개의 엔티티를 구성하는 것을 긍정적으로 검토해야 한다.
        • 즉시 갱신되지 않아도 될 경우, 결과적 일관성을 사용해 다른 애그리게잇을 갱신한다.
      • 대부분의 경우 즉시 갱신되지 않아도 되는 경우일 것이다.
      • 갱신의 수용 가능한 소요 시간을 결정하는 것은 기술이 아닌 비즈니스다.

6장. 도메인 이벤트와 전술적 설계

  • 도메인 이벤트는 바운디드 컨텍스트 내의 비즈니스 관점에서 중요한 사항들에 대한 기록이다.
  • 도메인 이벤트를 설계, 구현, 사용하기
    • 도메인 이벤트 역시 보편언어를 반영해야 한다.
    • 도메인 이벤트의 이름은 과거형 동사로 짓는다.
      • ex. ProductCreated, SprintScheduled, BacklogItemCommitted
    • 도메인 이벤트는 명령을 통해 발생된다. 명령은 현재형 동사로 짓는다.
      • ex. CreateProduct,
    • 도메인 이벤트는 이벤트가 만들어지는 시점에, 명령이 제공하는 모든 프로퍼티를 담고 있어야 한다.
      • 이렇게 해야 정확히 무슨 일이 벌어졌는지 구독자들에게 알릴 수 있다.
    • 도메인 이벤트가 풍부한 추가 데이터를 담고 있을 수도 있다. 하지만 그 의미를 잃을 정도로 너무 많은 데이터를 가득 챠우는 일이 없도록 주의해야 한다.
    • 애그리게잇을 하나의 테이블에, 그리고 도메인 이벤트를 이벤트 리포지토리 테이블에 저장하고 난 후 트랜잭션을 설정할 수 있다.
    • 만약 이벤트 소싱을 사용한다면 애그리게잇의 상태는 도메인 이벤트 자체로 온전히 표현할 수 있다.
  • 이벤트 소싱
    • 이벤트 소싱은 애그리게잇 인스턴스에 대해 변경된 것에 대한 기록으로, 발생했던 모든 도메인 이벤트를 저장하는 것을 말한다.
    • 하나의 애그리게잇 인스턴스에 발생했던 모든 도메인 이벤트를 발생한 순서대로 이벤트 스트림에 구성한다.
    • 이벤트 리포지토리는 모든 도메인 이벤트를 추가하는 순차적인 리파지토리 컬렉션 또는 테이블을 말한다.
      • 이벤트 스토어는 오직 추가만 가능하다.
      • 따라서 매우 빠르게 동작한다.
    • 이벤트 소싱을 사용하면 CQRS를 사용할 수 밖에 없게 된다.

7장. 가속화와 관리 도구

7장에서는 프로젝트 초기 설계 및 프로젝트 관리 도구 등에 대해서 소개하는데, DDD 보다는 애자일 프로세스에 가까운 소개인 거 같아, 여기에서는 생략한다. 다만 이벤트 스토밍은 꽤 중요한 내용이라고 생각하는데, SK C&C 기술 블로그에 이벤트 스토밍에 대해 잘 써놓은 글이 있어 이에 대한 링크만 남긴다.


후기

최근에 회사에서 "해리, 밥님의 파이썬으로 살펴보는 아키텍처 패턴 (2021)" 를 읽고 정리하는 스터디를 마쳤다. 이 책은 간단한 아키텍처로 시작하여, DDD에 기반한 벤트 드라이븐 아키텍처로 발전시키는 내용을 담고있다. 스터디 동안 2번 정도 정리하며 읽은거 같은데, 내용이 매우 좋기는 하나 시스템이 좀 더 커지고 복잡해질 때 어떻게 설계를 더 발전시킬 것인 지에 대한 감이 오지 않았다. 무엇보다 DDD에서의 바운디드 컨텍스트와 애그리게이트, 그리고 프로젝트 구조에 대해서 뚜렷하게 머릿 속에 정리되지 않은 느낌이었다.

그래서 DDD에 대해서 한번 완벽 정리할 수 있는 책을 읽어야겠다 싶었다. 원래는 이전에 이미 사놓은 반 버논님의 도메인 주도 설계 구현 (IDDD, 2016) 를 읽으려다가, 책이 너무 두껍기도 하고 좀 더 빠르고 라이트하게 읽을 수 있는 책이 없나 살펴봤다. 그러다 발견한 책이 이 책이었다. 이 책 역시 반 버논님이 쓴 책이라 신뢰가 가기도 했고, 서론에 보면 반 버논님이 IDDD책의 핵심만 간추린 내용이라고 언급도 했다. 책 분량도 190페이지 정도로 짧아서 그냥 추석 연휴기간에 다 읽게 되었다.

책에서는 단순한 다이어그램으로 같이 설명해주기 때문에 내용에 대한 이해는 쉬웠다. 다만 DDD를 처음 접하는 사람들에게는 어려울 거 같다. 책에서는 DDD에서 말하는 개념들을 독자가 이미 알고있다고 생각하고 내용을 전개한다. 때문에 나같이 DDD 입문 서적이나 기본 개념들을 알고 있는 상태에서 다시 DDD의 큰 그림을 정리할 겸 보고 싶은 사람들이 읽으면 좋을 책이라 생각이 된다. 무엇보다 책이 얆아서, 헷갈리거나 까먹은 내용을 다시 빠르게 찾고 개념을 기억할 수 있지 않을까 싶다.

책을 읽었지만, DDD는 여전히 내게는 어렵다. 사실 더 어려워진거 같다. 도메인 주도 설계라는 이름 답게, 정말 "도메인"에 대한 이야기부터 시작해야 한다. 회사 차원에서 핵심 도메인이 무엇인지 부터 파악하고, 이에 대한 큰 맵을 그리는 일은 회사 테크 리드급이 할 수 있는 일이지 않을까 싶기도 하다. 이 책을 읽고난 후 내가 회사에서 맡은 도메인은 서브 도메인의 일부 였음을 알게 되었고, 내가 여기에서 DDD적으로 어떤 것을 시작하고 결정할 수 있는 지 모르겠다는 느낌을 받았다.

이벤트 스토밍과 같은 프로세스는 혼자서 한다고 할 수 있는 일도 아니고, 일종의 회사 내 문화가 되어야 가능한 일이라고 생각한다. 또 한다고 하더라도, 누군가 경험있는 사람이나 전문가가 도와줘야 되지 않을까 싶은 생각이 들었다. 내가 잘 배우고 시도해보면서 역량을 키워볼 수도 있는 일이지만, 글쎄... 나도 막상 내가 진행하면 경험이 없는 터라 확신이 없어 무섭기도 하다. 일단은 내가 혼자서 해볼 수 있는거라도 시도해봐야겠다.

DDD에서 말하는 개념과 방법에 대해서는 이제 머리 속에 정리가 좀 되었으니, 이제 구현에 대해서 좀 생각해봐야겠다.