나에게 도메인 주도 설계는

설계는 단지 어떻게 생겼는지, 어떤 느낌인지가 아니라, 그게 어떻게 동작하는지에 대한 것이다. - 스티브 잡스

전략적 설계

  • 비즈니스 상 전략적으로 중요한 것
  • 중요도에 따라 일을 나누는 방법
  • 필요에 따라 통합하는 최적의 방법
  • Bounded Context를 사용해서 도메인 모델을 분리하는 방법
  • Bounded Context 안의 도메인 모델에서 보편언어를 개발하는 방법
  • Subdomains이 무엇이고
  • 어떻게 Subdomains이 기존 시스템의 제한되지 않은 복잡성을 다룰 수 있게 도와주는지
  • 앞으로 진행할 프로젝트 결과를 어떻게 향상시킬 수 있는지
  • Context Mapping이라는 기술을 통해 여러 개의 Bounded Context를 통합하는 방법
  • Context Map은 2개의 Bounded Context를 통합하면서 그 사이에 존재하는 팀의 관계, 기술적 메커니즘을 정의한다.

전술적 설계

  • EntityValue Object를 알맞은 크기의 Aggregate으로 묶는데 사용하는 Aggregate 패턴
  • Domain Events의 사용은 명확하게 모델링하는 것을 도와주면서, 도메인에 발생한 것에 대해 알아야 하는 내용을 시스템과 공유하는 것을 돕는다.
  • 공유할 대상이 로컬의 Bounded Context일 수도, 다른 원격의 Bounded Context일 수도 있다.

Bounded Context보편언어와 전략적 설계

DDD는 주로 명확하게 Bounded Context 내에서 보편언어를 모델링하는 것에 대한 것이다.

Bounded Context

  • 모델이 구현되는 곳으로 특정한 의미를 갖고, 특정한 일을 수행한다.
  • Bounded Context 마다 각각 분리된 소프트웨어 산출물이 나온다.

    보편언어

  • 도메인 전문가와 개발자가 공통으로 사용하는 언어
  • 엄격, 정확, 엄중, 단호해야 한다.

    핵심 도메인

  • 조직의 핵심 전략적 계획으로 개발되고 있는 Bounded Context
  • 무엇이 핵심 도메인이어야 하고, 어떤 것을 제외시켜야 하는지 현명하게 선택해야 한다.

중요한 위험 요소

너무 많은 것을 하나의 모델에 넣는 것과 큰 진흙 덩어리를 만드는 것

  • 시스템의 명확한 경계 없이 여러 개의 뒤엉킨 모델들을 담고 있다.
  • 서로 관련이 없는 다양한 개념들이 수많은 모듈로 확장
  • 어울리지 않는 모듈을 상호 연계
  • 테스트를 수행하는데 아주 오랜 시간이 걸림

DDD 전략적 설계의 적용

  • 서로 다른 개념들을 각기 다른 Bounded Context 안으로 분리해 놓음으로써 개념 간 차이를 중시한다.
  • '정책'에 3가지 의미가 존재한다면, 3개의 Bounded Context들은 각각 고유한 정책적 특성을 포함한 정책을 갖는다.

Bounded Context보편언어의 사용

  • Bounded Context
    • '핵심이 무엇인가?'라는 질문을 계속 던져보자.
    • 핵심이 되는 개념들은 팀이 사용하는 보편언어의 일부가 된다.
    • 나머지 개념들은 모두 제외시켜야 한다.
  • 핵심 도메인에서 제외시켰던 다른 모델링 개념들 중 몇몇은 각각의 Bounded Context 내에 정의될 가능성이 높고, 각각의 보편언어에 연결될 것이다.
  • Context Mapping을 이용해 이들을 통합한다.

Bounded Context 내에서 발견할 수 있는 아키텍처 컴포넌트

  • Bounded Context는 도메인 모델 이상의 다양한 요소들로 구성된다.
    • 예: 사용자 인터페이스 컨트롤러, REST endpoints, message listeners와 같은 Input Adapters, 유스케이스를 조율하고 트랜잭션을 관리하는 Application services, ...

서브도메인과 전략적 설계

서브도메인

서브도메인이란

  • 전체 비즈니스 도메인의 하위 부분
  • 하나의 논리적 도메인 모델을 나타내는 것
  • 전체 비즈니스 도메인을 논리적으로 쪼개는 데 서브도메인을 사용할 수 있다.

서브도메인의 유형

  • 핵심 도메인
    • 보편언어를 신중하게 만들기 위한 전략적 투자 영역
    • 주요 자원을 할당하는 명시적인 Bounded Context 이다.
  • 지원 서브도메인
    • 이미 존재하는 제품으로 해결할 수 없는 맞춤 제작 개발이 필요한 모델링 영역을 말한다.
    • 아웃소싱을 고려해볼 수도 있다.
  • 일반 서브도메인
    • 기존 제품 구매를 통해 바로 충족 시킬 수 있는 경우에 해당한다.
    • 아웃소싱을 할 수도 있고, 직접 개발할 수도 있다.
    • 일반 서브도메인핵심 도메인으로 오해하지 않도록 주의하자.

Bounded Context서브도메인을 1:1 관계로 맺자

  • Bounded Context를 정확하게 유지시키고 핵심 전략 목표에 집중하는 데 도움을 준다.

핵심 도메인과 지원 서브도메인을 2개의 서로 다른 Bounded Context로 분리하는 것이 현실적이지 않을 때, DDD 모듈로 그것을 분리하자

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

컨텍스트 매핑의 종류

파트너십

  • 두 팀이 함께 성공하거나 다같이 실패한다.
  • 상호간 의존적인 작업이나 여러 일정들을 조율하고, 통합을 적절하게 유지하기 위해 지속적으로 통합에 노력한다.

고객-공급자

  • 2개의 Bounded Context와 각 팀들의 관계를 나타낸다.
  • 고객이 언제 무엇을 받게 될지는 결국 공급자가 정한다.

준수자

  • 하류 팀이 현재의 상류팀 모델을 그대로 따른다.
  • 예: 아마존과 제휴하는 판매자 중 하나가 아마존 시스템과 통합하려고 할 때 아마존 모델을 준수한다.

반부패 계층

  • 가장 방어적인 컨텍스트 매핑 관계이다.
  • 하류 팀이 그들의 보편언어 모델과 상류 팀의 보편언어 모델 사이에 번역 계층을 만드는 것이다.
  • 이 계층은 상류 모델로부터 하류 모델을 독립시키고 둘 사이를 번역한다.

공개 호스트 서비스

  • 일련의 서비스처럼 Bounded Context에 대한 접근을 제공하는 프로토콜이나 인터페이스를 정의한다.
  • 보통 서드파티에게 공표된 언어를 제공한다.

공표된 언어

  • 이를 사용하는 Bounded Context의 규모에 관계없이, 모두 간단한 사용과 번역을 가능하게 하는 정보 교환 언어이다.
  • XML 스키마, JSON 스키마처럼 좀 더 최적화된 작성 형식으로 정의할 수 있다.
  • 보편언어 사이에 번역을 제공한다.

컨텍스트 매핑 통합 방법

SOAP을 이용한 RPC

  • 원격 프로시저 호출인 RPC는 다양한 방법으로 동작한다.
  • RPC의 잘 알려진 사용법 중 하나는 SOAP을 이용하는 방법이다.
  • RPC 사용의 문제점
    • 네트워크나 SOAP API를 호스팅하는 시스템에 문제가 생기면 호출은 실패한다.
    • SOAP을 이용한 RPC는 클라이언트 Bounded Context와 서비스를 제공하는 Bounded Context 사이의 강한 결합을 암시한다.
  • 원치 않는 외부의 영향으로부터 클라이언트 Bounded Context를 분리할 필요가 있다면, 반부패 계층을 정의하자.

RESTful HTTP

  • Bounded Context 간에 교환되는 리소스 뿐만 아니라 POST, GET, PUT, DELETE 네 가지 주요 오퍼레이션들이 관여된다.
  • 공표된 언어로 리소스를 정의하고 REST URI로 구성하면 공개 호스트 서비스를 구성할 수 있다.
  • 도메인 모델 안에 직접적으로 Aggregate을 반영하는 리소스를 설계하지 말자.
    • 실제 있는 그대로의 도메인 모델이 아니라 클라이언트에게 제공하는 리소스가 그들이 원하는 것에 대한 구성과 형태를 갖도록 고려해야 한다.
    • 클라이언트가 원하는 것은 모델의 지금 현재 구성이 아니라 리소스의 설계를 활용하는 것이다.

메시징

  • RPC나 REST와 달리, 분절된 형태와의 일시적인 결합을 대부분 제거할 수 있다.
  • 메시지 교환에서는 지연 가능성이 있기 때문에 즉각적인 결과가 필수 적이지 않을 때 사용하면 좋다.
  • 구독 Bounded Context도메인 이벤트를 받으면, 이벤트 형태와 값을 토대로 동작을 수행한다.
  • 특정한 수행을 위해 발행 Bounded Context에 커맨드 메시지를 보낼 수도 있다.
  • 구독 Bounded Context는 멱등 수신자로 구현되야 한다.
  • 메시징으로 설계하면 전체 솔루션을 매우 견고하게 만들 수 있다.

컨텍스트 매핑 구축

사례

  • '계약 심사' 컨텍스트에서 '정책' 컴포넌트가 만들어진다.
  • 정책 발행이라는 이름의 도메인 이벤트를 발생시킬 수 있다.
    • 이 이벤트는 정책 ID를 통해 정책들을 식별한다.
  • 메시징 구독을 통해 이를 제공받은 구독 Bounded Context 안에 이 정책에 상응하는 '정책' 컴포넌트를 만들 수 있다.
  • 구독 Bounded Context에 생성된 모든 컴포넌트는 발신 주체인 '계약 심사' 컨텍스트로의 역추적을 위해 정책 ID(식별자)를 보유한다.
  • 이제 '정책' 발행 ID로 쿼리를 수행하여 언제든지 '계약 심사' 컨텍스트로부터 더 많은 정보를 가져올 수 있다.

모두 담는 것과 다시 쿼리하는 것 사이의 장단점

  • 모두 담는 것
    • 소비자들에게 큰 자율성을 허용
    • 보안적인 측면이 부실해질 수 있다.
  • 다시 쿼리하는 것
    • 가벼운 도메인 이벤트
    • 소비자들이 높은 보안 수준에서 요청할 수 있다.

Aggregate과 전술적 설계

Entity

  • 독립적인 것
  • Entity는 같은 형태를 띠거나 다른 형태의 Entity들과의 특성을 구별할 수 있는 고유한 식별성을 갖는다.
  • 변할 수 있는 것이며, 상태는 계속해서 변할 수 있다.

Aggregate

  • 1개 이상의 Entity로 구성된다.
  • 그중 한 EntityAggregate 루트라고 부른다.
    • 루트 EntityAggregate 안의 다른 모든 요소를 소유한다.
    • 루트 Entity의 명칭은 Aggregate의 개념적 명칭이다.
  • 구성에 Value Object를 포함할 수 있다.

Value Object

  • 불변의 개념적 완전성을 모델링한 것이다.
  • Entity와 달리 고유한 식별성이 없다.
  • 값 형태로 캡슐화 된 속성을 비교함으로써 동일함이 결정된다.
  • Value Object가 어떤 것을 나타낸다기보다는 Entity를 서술하고, 수량화하거나 측정하는데 사용된다.

Aggregate의 특징

  • Aggregate은 일관성 있는 트랜잭션 경계를 형성한다.
    • 트랜잭션은 Aggregate에 대한 변경을 독립시키고, 비즈니스 불변성을 각 비즈니스 오퍼레이션에 맞게 일관성을 보장하는 방법이다.
    • Aggregate의 상태나 이벤트 소싱은 항상 안전하고 정확하게 트랜잭션으로 처리하고 관리해야 한다.

Aggregate 설계의 기본 규칙

  1. Aggregate 경계 내에서 비즈니스 불변사항들을 보호하라.
  2. 작은 Aggregate을 설계하라.
    • Aggregate의 메모리 사용량과 트랜잭션 범위가 비교적 작아야 한다.
    • 빠르게 로드되고 가비지 컬렉션도 더 빨라지며 테스트도 쉬워진다.
    • SRP를 따르는지 항상 생각하자.
  3. 오직 ID를 통해 다른 Aggregate을 참고하라.
    • 동일한 트랜잭션 내에 다른 Aggregate을 수정하지 않게 해준다.
    • 여러 형태의 저장 메커니즘 모두 쉽게 저장할 수 있다.
  4. 결과적 일관성을 사용해 다른 Aggregate을 갱신하라.
    • 결과적 일관성이란?
      • 분산 시스템에서 데이터를 조회할 때 모든 시스템이 동일한 데이터를 가질 수 있다고 보장할 수는 없다.
      • 결과적 일관성은 어느 시점에는 데이터가 다를 수 있지만, 결국에는 모든 시스템이 최신의 데이터를 가질 수 있도록 보장된다는 내용이다.
    • Aggregate의 트랜잭션의 일부로 구독한 Bounded Context들에게 도메인 이벤트를 발행시킬 수 있다.
      • 도메인 이벤트를 발행한 컨텍스트가 본인을 구독할 수도 있다.

Aggregate 모델링

  • 비즈니스 로직은 도메인 모델 안에서 정의해야 한다.
  • Aggregate의 모든 부분은 보편언어에 따라 모델링해야 한다.
  • 모델링하는 각 개념마다 적절한 수준의 추상화를 선택하자.
  • 올바른 크기의 Aggregate(일관성 경계 목표에 도달하는 설계 단계)
    1. 작은 Aggregate을 설계하라.
    2. Aggregate 경계 내에서 비즈니스 불변사항들을 보호하라.
    3. 반응에 맞춘 갱신이 일어나는 시간은 얼마나 걸릴지 도메인 전문가에게 확인하자.
    4. 각각의 Aggregate이 즉시 처리돼야 할 경우, 동일한 Aggregate 경계 안에 그 2개의 Entity를 구성하는 것을 긍정적으로 검토해야 한다.
    5. 각각의 Aggregate이 주어진 시간에 따라 각각 반응하는 경우, 결과적 일관성을 사용해 다른 Aggregate을 갱신하라.
  • 단위 테스트를 위해 Aggregate을 철저하게 캡술화되도록 설계하자.

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

도메인 이벤트 설계, 구현, 사용하기

도메인 이벤트를 생성, 이름 붙이기

  • 도메인 모델의 보편언어를 반영해야 한다.
  • 도메인 이벤트에 제대로 이름 붙이는 것은 굉장히 중요하다.
  • 도메인 이벤트 타입을 나타내는 이름
    • 과거형 동사로 표현하자.
    • 예: ProductCreated, SprintScheduled

      표준 도메인 이벤트 인터페이스를 정의하고 구현

      // 모든 **도메인 이벤트**가 반드시 지원해야 하는 최소한의 인터페이스
      // 일반적으로 **도메인 이벤트**가 발생할 때 그 날짜와 시각을 전달하길 원하는데, 이를 위해 OccurredOn 프로퍼티를 제공한다.
      public interface DomainEvent {
      public Date OccurredOn {
          get;
      }
      }

      도메인 이벤트의 프로퍼티를 정의하는 방법

  • '애플리케이션에서 어떤 것들이 도메인 이벤트를 발생시킬까?' 질문해보자.
  • 도메인 이벤트 타입은 이벤트가 만들어지는 시점에, 명령이 제공하는 모든 프로퍼티들을 담고 있어야 한다.

이벤트 리파지토리에 도메인 이벤트를 저장하는 방법

  • 이벤트 리파지토리란 ?
    • 모든 도메인 이벤트를 추가하는 순차적인 리파지토리 컬렉션 또는 테이블
    • 오직 추가만 가능해서 리파지토리 메커니즘은 매우 빠르게 동작한다.
    • 성능을 고려한다면 캐싱과 스탭샷에 대해 알고 있는 것이 좋다.
  • Aggregate을 하나의 테이블에 그리고 도메인 이벤트를 입엔트 리파지토리 테이블에 저장하고 난 후, 트랜잭션을 설정할 수 있다.
  • 이벤트 소싱을 사용한다면, Aggregate의 상태는 도메인 이벤트 자체로 온전히 표현할 수 있다.
  • 도메인 이벤트를 이벤트 리파지토리에 유지하는 것은 도메인 모델 간에 발생한 것에 대한 인과관계의 순서를 지속시켜준다.

도메인 이벤트 저장 후 이를 발생시키는 방법

  • 일부 도메인 이벤트는 명령에 의해 유발될 수 있고, 다른 경우에는 일자, 시간과 같은 상태가 변경되는 것들의 인지를 통해 유발될 수 있다.

이벤트 소싱

  • Aggregate 인스턴스에 대해 변경된 것에 대한 기록으로, 발생했던 모든 도메인 이벤트를 저장하는 것을 말한다.
  • 즉, Aggregate 상태 전체를 저장하는 대신, 발생했던 각 도메인 이벤트 모두를 저장한다.
  • 큰 이점 중 하나는 핵심 도메인에서 계속 발생하는 모든 기록을 개별적인 발생 수준으로 저장한다는 점이다.
    • 법적 기준에 대한 준수 및 분석이 가능
    • 디버깅하거나 이벤트 사용 추세를 조사할 수 있는 등 기술적인 이점이 있다.

+ 따끈한 최근 게시물