Skip to content

Latest commit

 

History

History
179 lines (146 loc) · 18.7 KB

도메인_주도_설계_핵심.md

File metadata and controls

179 lines (146 loc) · 18.7 KB

도메인 주도 설계 핵심

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

전술적 설계

  • 전술적 설계는 도메인 모델의 세부사항들을 그리기 위해 얇은 붓을 사용하는 것과 같다.
  • 가장 중요한 도구 중 하나는 Entity``와 Value Object를 알맞은 크기의 Aggregate으로 묶는 데 사용한느 Aggregate 패턴`이다.
  • DDD는 도메인을 최대한 명확한 방법으로 모델링하는 거셍 관한 것이다.
  • 도메인 이벤트의 사용은 명확하게 모델링하는 것을 도와주면서, 도메인에 발생한 것에 대해 알아야 하는 내용을 시스템과 공유하는 것을 돕는다.
  • 공유할 대상이 로컬의 바운디드 컨텍스트일 수도, 다른 원격의 바운디드 컨텍스트일 수도 있다.

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

엔티티는 무엇인가?

  • 엔티티는 독립적인 것이다.
  • 각 엔티티는 같은 형태를 띠거나 다른 형태의 엔티티들과의 특성을 구별할 수 있는 고유한 식별성을 갖는다.
  • 엔티티는 변할 수도 있고, 변하지 않을 수도 있다.
  • 다른 모델링 수단들과 엔티티를 구분해주는 주 요인은 유일성, 즉 그것의 독립성에 있다.

값 객체는 무엇인가?

  • 값 객체 또는 간단히 말해 값은 불변의 개념적 완전성을 모델링한다.
  • 모델에서 값은 그야말로 값이다.
  • 엔티티와 달리, 공유한 식별성이 없으며, 값 형태로 캡슐화된 속성을 비교함으로써 동일함이 결정된다.
  • 그뿐만 아니라 값 객체가 어떤 것을 나타낸다기보다는 엔티티를 서술하고, 수량화하거나 측정하는데 사용된다.

애그리게잇이란?

  • 각 애그리게잇은 1개 이상의 엔티티로 구성되고, 그 중 한 엔티티는 애그리게잇 루트라고 부른다. 또한, 그 구성에 값 객체를 포할 수 있다.

  • 각 애그리게잇의 루트 엔티티는 애그리게잇 안의 다른 모든 요소를 소유한다. (루트 엔티티의 명칭은 애그리게잇의 개념적 명칭이다.)

  • 애그리게잇이 모델링하는 개념적 완전성을 적절하게 표현할 수 있는 명칭으로 루트 엔티티 명칭을 정의해야 한다.

  • 각 애그리게잇은 일관성 있는 트랜잭션 경계를 형성한다.

  • 이것은 트랜잭션 제어가 데이터베이스에 커밋될 떄, 한 어그리게잇 내의 모든 구성 요소는 반드시 비즈니스 규칙을 따르면서, 일관성 있게 처리된다는 것을 의미한다.

애그리게잇 경험 법칙

  • 애그리게잇 설계의 4가지 기본 규칙은 다음과 같다.
  1. 애그리게잇 경계 내에서 비즈니스 불변사항들을 보호하라.
  2. 작은 애그리게잇을 설계하라
  3. 오직 ID를 통해 다른 애그리게잇을 참고하라.
  4. 결과적 일관성을 사용해 다른 애그리게잇을 갱신하라
  • 위의 규칙들을 반드시 엄격하게 지키지 않아도 된다.
  • DDD를 신중하게 적용할 때, 효과적으로 동작하는 애그리게잇을 설계할 수 있도록 도와주는 가이드로 볼 수 있다.

애그리게잇 설계 규칙 1. 애그리게잇 경계 내에서 비즈니스 불변사항들을 보호하라.

  • 규칙 1은 결과적으로 트랜잭션이 커밋될 떄 비즈니스의 일관성이 지켜지는 것에 기반을 두고, 애그리게잇 구성 요소를 결정해야 한다는 의미이다.
스크린샷 2023-10-13 오후 4 48 07
  • 위 사례를 예시로 들어보면, Product는 트랜잭션 끝에 ProductBacklogItem 인스턴스로 구성되는 모든 것이 반드시 Product의 루트와 일관되게 처리되도록 설계한다.
  • 또한, Sprint는 트랜잭션의 끝에 CommittedBacklogItem 인스턴스로 구성되는 모든 것이 반드시 Sprint 루트와 일관되게 처리되도록 설계한다.

애그리게잇 설계 규칙 2. 작은 애그리게잇을 설계하라

  • 이 규칙은 각 애그리게잇의 메모리 사용량과 트랜잭션 범위가 비교적 작아야 함을 강조한다.
스크린샷 2023-10-13 오후 4 55 14
  • 위 다이어그램에 표현된 애그리게잇은 작지 않다.

  • 여기서, Product는 문자 그대로 BacklogItem 인스턴스들 그리고, Sprint의 가능한 가장 큰 모음을 담고 있다.

  • 일반적으로 이런 설계 방식은 매우 나쁜 선택이다.

  • 왜냐하면, 이런 모음들은 시간이 지나면서 1000여 개의 BacklogItem 인스턴스와 약 100여 개의 Release, Sprint 인스턴스로 엄청나게 크게 불어날 수 있기 때문이다.

  • 하지만, 아래 다이어그램처럼, Product 애그리게잇을 4개의 애그리게잇으로 구성하는 형태로 분해할 수 있다.

스크린샷 2023-10-13 오후 4 55 35
  • 이들은 빠르게 로드되고, 더 작은 메모리를 차지하며, 가비지 컬렉션도 더 빠르다.
  • 하지만, 가장 중요한 것은 이 애그리게잇들은 이전의 큰 클러스터의 Product 애그리게잇보다 훨씬 더 자주, 성공적인 트랜잭션을 수행할 것이라는 점이다.
  • 이 규칙을 따르면, 연관된 각 작업이 한 명의 개발자가 관리할 수 있을만큼 작기 때문에, 각 애그리게잇이 좀 더 쉬워지는 부가적인 이득을 얻을 수 있으며, 테스트 또한 보다 더 쉬워질 것이다.

애그리게잇을 설계할 때 새겨둬야 할 사항 하나가 SRP라는 단일 책임 원칙이다.

  • 만일 애그리게잇이 너무 많은 일을 한다면, 이는 SRP를 따르지 않는 것이고, 이후 애그리게잇의 크기에 대해 재논의할 가능성이 크다.
  • 예를 들어, 만들고자 하는 Product가 스크럼 제품에 주안점을 두고 있는지, 아니면 다른 것도 함께 추구하는 것인지
  • 즉, Product를 변경하는 이유가 더 나은 스크럼 제품을 만들기 위해서인지? 아니면 백로그 아이템, 릴리스, 스프린트를 관리하기 위해서인지? 에 대해 고민해보면, 답을 알 수 있다.
  • 여기에서는 더 나은 스크럼 제품을 만들기 위해 Product를 바꿔야 한다는 목적에 초점을 둬야 한다.

애그리게잇 설계 규칙 3. 오직 ID를 통해 다른 애그리게잇을 참고하라.

  • 이러한 형태는 애그리게잇을 작게 유지하고, 동일한 트랜잭션 내에 여러 애그리게잇을 수정하려는 접근을 방지해준다.
  • 이렇게 식별자을 통해서만 레퍼런스를 얻는 규칙의 장점은 다음과 같다.
  1. 더 적은 메모리 요구와 리파지토리로부터의 빠른 로딩을 통해 애그리게잇 설계를 작고 효율적으로 유지할 수 있게 해준다.
  2. 동일한 트랜잭션 내에 다른 애그리게잇을 수정하지 않는 규칙이 잘 지켜지도록 해준다.
  3. 애그리게잇을 관계형 데이터베이스, 문서 데이터베이스, 키/밸류 리파지토리 그리고 데이터 그리드/패브릭과 같은 다른 형태의 저장 메커니즘으로도 쉽게 저장할 수 있다는 것이다.
    • 이는 MySQL 관계형 테이블, PostgresSQL이나 MongoDB, GemFire/Geode, Coherence 그리고 GigaSpaces와 같은 JSON 기반의 리파지토리 사용을 선택적으로 결정할 수 있다는 것을 의미한다.

애그리게잇 설계 규칙 4. 결과적 일관성을 사용해 다른 애그리게잇을 갱신하라

  • BacklogItem은 Sprint와 연계되어 수행된다.
  • 그래서, BacklogItem과 Sprint 모두 이것에 맞춰 설계가 이루어진다.
  • 먼저, BacklogItem은 관여된 Sprint를 알아야 한다.
  • 이는 BacklogItem의 상태가 해당 Sprint의 SprintID를 갖도록 정의하는 하나의 트랜잭션 안에서 관리된다.

결과적 일관성이 두럽게 느껴진다면

  • 결과적 일관성을 사용함에 있어 엄청나게 힘든 점은 없지만, 실제 경험해보기 전까지는 결과적 일관성 사용에 대한 걱정이 있을 수도 있다.
  • 그렇다고 해도 비즈니스에 의해 정의된 트랜잭션 경계에 따라 모델을 애그리게잇으로 분리시켜야 한다.
  • 2개 이상의 애그리게잇을 단일한 데이터베이스 트랜잭션으로 묶어 처리하고 싶어 할 수도 있다.
  • 이전에 이철머 커다란 트랜잭션을 처리했고, 성공했을 수도 있지만, 다른 모두를 위해 일관성 있게 사용할 필요가 있다.
  • 이는 초기 단계부터 너무 거대한 처리를 만들지 않도록 해주는 기법이다.
  • 이것이 애그리게잇을 사용하는 근본적인 이유는 아니지만, 결과적으로 트랜잭션 실패 경험을 줄여줄 수는 있을 것이다.

애그리게잇 모델링

  • DDD를 사용할 때 바운디드 컨텍스트 내의 보편언어를 모델링한다는 것을 항상 기억해야 한다.
  • 따라서, Product 애그리게잇의 모든 부분은 보편언어에 따라 모델링해야 한다.
  • 모든 것이 조화를 이룰 수 있도록 도메인 전문가와 개발자들 사이에 긴밀한 협업이 필요하다.

추상화를 조심스럽게 선택하라

  • 효과적인 소프트웨어 모델은 항상 일을 하는 비즈니스의 방식을 고려한 일련의 추상화에 기반을 두고 있다.
  • 이떄 모델링하는 각 개념마다 적절한 수준의 추상화를 선택해야 한다.
  • 만일, 보편언어와 관련된 가이드를 따른다면 적절한 추상화를 설정할 수 있다.
  • 적어도 모델링 언어의 기반에 지식을 전달해주는 도메인 전문가가 있기 때문에, 훨씬 정확하게 추상화를 모델링할 수 있다.
  • 하지만, 가끔은 잘못된 문제를 푸는 것에 지나치게 몰두한 나머지, 소프트웨어 개발자가 지나칠 정도로 추상화를 적용하기도 한다.
  • 이런 잘못된 방식을 따르는 상황이 나타날지 의문을 갖는 사람들도 있겠지만, 이런 부적절한 추상화 수준은 기술적인 측면으로 구현을 생각하는 상황에서 자주 등장한다.
  • 팀이 정의한 도메인 저문가의 멘탈 모델에 따라 보편언어를 모델링해야 한다.
  • 비즈니스가 지금 당장 요구하는 것을 모델링하면 상당한 시간, 예산, 코드를 아끼고, 곤란한 상황에 빠지지 않을 수 있다.
  • 더 나아가 정확하고 유용한 바운디드 컨텍스트에 효과적인 설계를 반영한 모델링을 통해 대단히 멋진 서비스에 더 많은 기여를 하게 될 것이다.

올바른 크기의 애그리게잇

  • 비즈니스 불변사항을 보호할 일관성 경계는 여전히 유지하면서도, 애그리게잇의 경계를 결정하고, 큰 클러스터를 설계하는 것을 방지할 수 있을지 궁금할 것이다.
  • 만일, 이미 큰 클러스터의 애그리게잇을 만들었다면, 좀 더 작은 것들로 리팩토링하는 데 이 방법을 사용할 수도 있다.
  • 일관성 경계 모표에 도달하는 데 도움을 줄 아래 설계 단계들을 살펴보자.
  1. 먼저 애그리게잇 설계의 2번재 규칙인 "작은 애그리게잇을 설계하라"에 집중하자.
  • 애그리게잇 루트로 제공될 오직 1개의 엔티티만을 갖는 애그리게잇을 생성한다. (조만간 1개 이상의 엔티티를 둘 수 있지만, 그 경우에 대해서는 일단 생각하지 않는다)
  • 각 엔티티들을 단일의 루트 엔티티와 관련이 가장 깊다고 생각되는 필드/속성/프로퍼티로 채우자.
  • 여기서 가장 중의할 점은 애그리게잇을 식별하고 찾는 데 필요한 모든 필드/속성/프로퍼티를 정의하는 것 뿐만 아니라 애그리게잇을 초기에 만들 떄 유효한 초기 상태를 구성하는데 필요한 모든 추가적인 필드/속성/프로퍼티를 정의하는 것이다.
  1. 이제 애그리게잇 설계의 1번째 규칙인 "애그리게잇 경계 내의 비즈니스 불변사항을 보호하라"로 관심을 돌리자.
  • 이미 이전 단계에, 단일 엔티티 애그리게잇을 저장할 때 모든 필드/속성이 반드시 최신의 정보를 포함한 상태여야 한다는 것을 알았을 것이다.
  • 하지만, 지금은 애그리게잇을 한 번에 하나씩 살펴봐야 한다.
  • 애그리게잇 A1을 살펴본다고 할 때, 이미 정의한 다른 애그리게잇들 중에 A1 애그리게잇이 변경될 때 함께 갱신되어야 하는 것이 있는지 도메인 전문가에게 확인한다.
  • 애그리게잇의 행위에 관련된 모든 갱신에 걸리는 시간을 파악할 수 있는 관련된 각 애그리게잇의 목록과 일관성 규칙을 만든다.
  • 다시 말하면, "애그리게잇 A1"을 목록의 가장 앞에 위치시키고, A1의 변경에 따라 바뀌어야 할 다른 애그리게잇들을 그 아래에 위치시킨다.
  1. 반응에 맞춘 갱신이 일어나는 시간은 얼마나 걸릴지 도메인 전문가에게 확인하자.
    1. 즉시 또는 2) N초/분/시간/일과 같은 2가지 유형의 명세로 정의될 것이다.
  • 올바른 비즈니스 임계치를 찾는 1가지 가능한 방법은 받아들여질 수 없는 과장된 소요 시간(몇 주 또는 몇 달과 같은)을 먼저 제시하는 것이다.
  • 비즈니싀 전문가로 하여금 받아들일 수 있는 소요 시간을 답하게 만들 수 있을 것이다.
  1. 각각의 애그리게잇들이 즉시 처리되어야 할 경우, 동일한 애그리게잇 경계 안에 그 2개의 엔티티를 구성하는 것을 긍정적으로 검토해야 한다.
  • 그 의미는 예를 들어, 애그리게잇 A1과 애그리게잇 A2를 새로운 애그리게잇 A[1, 2]로 구성한다는 것이다.
  • 이전에 정의했던 애그리게잇 A1과 A2는 더 이상 존재하지 않고, 오직 애그리게잇 A[1, 2]만 존재한다.
  1. 각각의 애그리게잇들이 주어진 시간에 따라 각각 반응하는 경우, 애그리게잇 설계의 4번째 규칙인 "결과적 일관성을 사용해 다른 애그리게잇을 갱신하라"를 사용해서 갱신한다.
스크린샷 2023-10-13 오후 7 55 13
  • 이 그림에서 A1 관점에서 모델링을 살펴보자.
  • A1의 일관성 규칙 목록에는 A2가 적혀 있고, C14는 소요 시간(30초)를 갖는다.
  • 결과적으로 A1과 A2는 하나의 애그리게잇 A[1, 2] 안으로 통합 모델링된다.
  • 또한, 런타임 중에 애그리게잇 A[1, 2]는 애그리게잇 C14를 갱신하도록 하는 도메인 이벤트를 발행시킨다.
  • 모든 애그리게잇이 함께 즉각적인 갱신에 들어가야 한다고 비즈니스 측에서 일방적으로 주장하지는 않는지 주의를 기울여야 한다.
  • 설계 회의에 참여하는 많은 사람들이 DB 설계와 데이터 모델리에 영향을 받을 때 특히 강하게 이런 경항을 보일 수 있다.
  • 그 이해관계자들은 트랜잭션 위주의 관점을 가질 것이다.
  • 하지만, 실제로 비즈니스가 모든 상황에 즉각적인 일관성을 요구할 가능성은 매우 낮다.
  • 이런 생각을 바꾸기 위해 현재의 큰 클러스터 애그리게잇을 구성하는 여러 애그리게잇들에 걸친, 다수의 사용자들에 의해 동시에 발생하는 갱신들로 인해 어떻게 트랜잭션이 실패하게 될지 입증하는 데 시간을 보내는 상황이 발생할 수도 있다.
  • 그뿐만 아니라 그런 큰 클러스터 설계로 인해 얼마나 많은 메모리 오버헤드가 발생하는지에 대해 이야기를 해야 할 수도 있다.
  • 분명히 이런 문제들은 우선적으로 피하려고 노력해야 하는 것들이다.
  • 이런 활동은 결과적 일관성이 기술 주도가 아닌, 비즈니스 주도라는 것을 보여준다.
  • 물론, 이전 장의 컨텍스트 매핑에서 논했던 것처럼 다수의 애그리게잇 사이에 갱신을 위한 기술적인 방안도 찾아야 한다.
  • 그렇지만 다양한 엔티티 간에 발생하는 갱신의 수용 가능한 소요 시간을 결정할 수 있는 것은 오직 비즈니스다.
  • 이는 즉시 또는 적절한 트랜잭션으로 처리되어야 하는 것들은 동일한 애그리게잇으로 관리해야 한다는 의미다.
  • 또한, 결과적인 일관성이 필요한 경우에는 메시징과 같은 도메인 이벤트를 통해 관리해야 한다는 의미다.
  • 비즈니스가 해야 하는 일이 무엇인지를 고려하는 것은 매우 중요하다.
  • 실제 비즈니스가 어떤 일을 수행해야 하는지를 생각하는 것은 다양한 도메인 오퍼레이션들이 비즈니스 행위를 모델링한 소프트웨어에 통찰을 줄 것이고, 이는 비즈니스에 가치를 전달해줄 것이다.

테스트 가능한 단위

  • 단위 테스트를 위해 애그리게잇을 철저하게 캡슐화되도록 설계하자.
  • 복잡한 애그리게잇은 테스트하기도 힘들다.
  • 이전의 설계 가이드들은 테스트할 수 있는 애그리게잇을 모델링할 수 있는 좋은 지침들을 제공한다.
  • 단위 테스트는 비즈니스 명세 검증(인수 테스트)과는 다르다.
  • 단위 테스트 만드는 일은 인수 테스트에 관한 시나리오 명세 만드는 과정을 따라할 것이다.
  • 여기서 고려해야 하는 것은 애그리게잇이 수행하길 기대하는 대로 정확하게 수행되는지 테스트하는 것이다.
  • 또한, 애그리게잇의 모든 오퍼레이션이 정확성, 품질, 안정성을 보장하길 원할 것이다.
  • 이를 위해 단위 테스트 프레임워크를 사용할 수도 있고, 다른 효과적인 단위 테스트 방법에 대한 많은 자료들을 활용할 수도 있다.
  • 단위 테스트들은 바운디드 컨텍스트와 직접적으로 연관되는 것으로 해당 소스 코드 리파지토리에 보관될 것이다.

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

  • 도메인 이벤트는 바운디드 컨텍스트 내의 비즈니스 관점에서 중요한 사항들에 대한 기록이다.
  • 도메인 이벤트가 전략적 설계를 위해 매우 중요한 도구이다.
  • 그 뿐만 아니라 종종 전술적 설계를 하는 동안 도메인 이벤트의 개념이 정립되면서 핵심 도메인의 일부가 된다.

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

바운디드 컨텍스트 내에 도메인 이벤트를 효과적으로 설계하고 구현하는 방법과, 도메인 이벤트를 어떻게 사용하는지에 대한 사례를 살펴보자.