본문 바로가기

Design/Domain Driven Design

[DDD] 요약

요약

이번 장에서는 Domain-Driven Design(이하 DDD)를 본격적으로 알아보기 전에 DDD에 대해 전반적으로 알아보는 시간을 가져보도록 한다.


About Domain-Driven Design(DDD)

  • DDD는 2003년 Eric Evans에 작성한 책에 의해 유행하기 시작하였다.
  • DDD를 위한 패턴 자체를 제시하는 것은 유용하지만, DDD의 많은 미묘함은 패턴의 설명에서 많이 손실된다.
  • 이러한 패턴은 규칙이 아니라 DDD를 구현하기 위한 도구이며, 디자인을 위한 언어이며 팀 간의 아이디어와 모델을 전달하는 데 유용하다.
  • 더 중요한 것은 DDD를 통해 실용적인 결정을 내리는 방법을 제시하는 것이다.
  • 모델에 패턴을 강제하지 않도록 하고, 패턴을 지키지 않는 경우에는 타당한 이유를 다른 개발자에게 전달해야 한다.
  • DDD를 올바른 객체 지향으로 생각할 수 있지만, 단순한 객체 지향 그 이상이다.
  • DDD는 또한 문제 공간을 이해하는 도전과 그 이해를 전달하는 더 큰 도전을 다룬다.
  • 중요하게도 DDD는 TDD(테스트 주도 개발), 패턴 사용 및 지속적인 리팩토링과 같은 다른 영역의 포함을 권장한다.

Representing the Model

  • DDD는 디자인과 표현력이 뛰어는 모델 생성에 관한 것이다.
  • DDD는 또한 소프트웨어 개발자뿐만 아니라 소프트웨어 개발에 관련된 모든 사람이 이해할 수 있는 모델을 만드는 것을 목표로 한다.
  • IT와 관련되지 않은 사람도 이러한 모델로 작업하기 때문에 모델을 다양한 방식으로 표현할 수 있으면 편리하다.
  • 일반적으로 도메인 모델은 UML 스케치, 코드 및 도메인 언어로 묘사될 수 있다. 아래는 같은 모델을 여러 방식으로 표현한 예시이다.

Using Language

  • 교육과정 수강을 희망하는 사람은 주제, 비용, 과정 일정을 기준으로 과정을 검색한다.
  • 코스가 예약되면 나중에 취소하거나 수락할 수 있는 등록이 발생된다.

Using Code

class person {
    public Registration bookCourse(Course course) { ... }
}
abstract class Registration {
    public abstract void accept();
    public abstract void cancel();
}
class ReservedRegistration extends Registration { ... }
class AcceptedRegistration extends Registration { ... }
interface CourseRepository {
    public List<Course> find( ... )
}

Using a UML Sketch


Ubiquitous Language

  • 명확한 언어의 일관된 사용은 해당 영역에서 발견된 통찰력을 이해하고 전달하는 데 필수적이다.
  • DDD에서는 명사와 동사에 관한 것이 아니라 개념에 대해 더 많이 설명한다. 개념의 의도를 이해하고 전달하는 것에 중요한 의미와 가치가 있다.
  • 의도를 구현하는 방법은 중요하지만 모든 의도에는 많은 구현이 있다.
  • 모든 사람은 이러한 개념과 의도를 이해하고 공유할 수 있는 모든 기회와 장소에서 언어를 사용해야 한다.
  • 유비쿼터스 언어로 작업할 때 도메인 전문가와의 협업은 모두에게 더 창의적이고 가치가 있다.

실행이 아닌 의도를 드러내라(Reveal the Intention not the Implementation)

  • 도메인 전문가가 숨기거나 가정하는 중요한 개념을 모호하게 할 수 있는 언어의 기술 및 비즈니스 장애에 주의해야 한다.
  • 종종 이러한 용어는 도메인 개념이 아닌 구현을 다룬다.
  • DDD는 구현을 배제하지 않지만 모델의 의도를 더 높게 평가한다.
  • 아래의 대화를 살펴보도록 한다.
    • 사람이 강좌를 예약하고 강좌가 가득 찼을 때 그 사람은 ‘대기’상태가 된다.
    • 사용 가능한 공간이 있는 경우 결제 게이트웨이에서 처리할 수 있도록 메시지 버스를 통해 해당 개인의 세부 정보를 보내야 한다.
  • 아래는 위의 대화에 대한 몇 가지 잠재적인 방해 요소이다.
  • 이러한 용어는 가치를 추가하지는 않지만, 도메인을 더 깊이 파고들 수 있는 훌륭한 단서가 된다.
사람은 상태를 가지고 있다. 상태가 플래그 또는 필드처럼 보인다. 아마도 도메인 전문가는 스프레드 시트와 같은 다른 시스템에 익숙하고 이 구현을 제안하고 있을 것이다.
메시지 버스를 통해 전송한다. 이것은 기술의 구현이다. 메시지 버스를 통해 전송된다는 사실은 도메인에서 중요하지 않다.
처리 이것은 모호하고 애매하다. 처리하는 동안 어떤 일이 발생하는가?
결제 게이트웨이 또 다른 구현이다. 어떤 형태의 결제가 있다는 것이 더 중요하지만 결제의 구현은 현시점에서 중요하지 않다.

깊은 통찰력을 조준(Aim for Deep Insights)

  • 구현을 주시하고 실제 개념과 개념의 의도를 살펴본다.
  • 일부 구현 이면의 대화에 숨겨져 있을 수 있는 단서에 주의하면서 동일한 대화를 검토해 본다.
    • 사람이 강좌를 예약하고 강좌가 가득 찼을 때 그 사람은 ‘대기’상태가 된다.
    • 사용 가능한 공간이 있는 경우 결제 게이트웨이에서 처리할 수 있도록 메시지 버스를 통해 해당 개인의 세부 정보를 보내야 한다.
  • 더 깊이 파고들면 강좌를 예약하는 사람의 상태가 없는 것을 알 수 있다.
  • 대신 강좌에 등록한 사람의 상태는 ‘등록’이다.
  • 강좌가 가득 차면 ‘대기 등록’이라는 상태가 있다.
  • 모든 ‘대기 등록’은 대기자 명단에서 관리된다.

언어 리팩토링(Refactor the Language)

  • 언어는 도메인 모델의 표현을 작성하는 데 사용되며, 코드도 마찬가지라는 점을 기억해야 한다.
  • 코드가 새로운 용어로 리팩토링되면 새 용어를 통합하도록 언어를 리팩토링해야 한다.
  • 용어가 나타내는 개념이 정의되고 도메인 전문가가 그 의도 및 사용에 동의하는지 확인한다.
  • 위의 대화를 리팩토링하여 강좌를 예약해 본다.
    • 개인이 강좌에 등록하면 ‘예약 등록’이 발행된다.
    • 빈자리가 있고 결제가 완료되면 ‘예약 접수’가 된다.
    • 강좌에 좌석이 없을 경우 예약된 등록은 ‘대기 등록’으로 대기자 명단에 올라간다.
    • 대기자 명단은 선착순으로 관리된다.

구체적인 작업 예시(Working with Concrete Examples)

  • 구체적인 예를 사용하여 도메인 전문가와 협업하는 것이 더 쉬운 경우가 많다.
  • 종종 Behaviour-Driven Development(행동 주도 개발, 이하 BDD) 스토리 및 시나리오 템플릿을 사용하여 도메인 예를 설명하는 것이 편리하다.
  • 아래는 위에서 살펴본 대화를 BDD 템플릿을 사용하여 표현한 구체적인 표현한 예시이다.
Story: 강좌 등록하기
훈련을 찾는 사람으로서
나는 강좌를 예약하고 싶다.
내가 배우고 나의 스킬을 향상시키기 위해서
  • 이야기에서 어떤 이점을 얻기 위해(’내 기술을 배우고 향상시키기’) 무언가를 성취하기를 원하는(’과정을 예약하기 위해’) 역할(’훈련을 찾는 사람’)이 설명된다.
  • 우리는 이야기가 있으므로, 이야기에 여러가지 시나리오가 있다. 강좌 예약이 가득 찼다고 가정해본다.
Scenario: 강좌가 꽉찼다.
파이선101 과정이 10석을 수용한다는 점을 감안할 때
파이썬101 등록이 확인된 사람은 이미 10명이다.
파이썬101에 등록할 때
그런 다음 파이썬101에 대한 대기 등록이 있어야 한다.
내 대기 등록이 대기자 명단에 있어야 한다.
  • 주어진 절은 시나리오의 상황을 설명한다. “~때” 라인은 시나리오에서 발생하는 이벤트이고 “그런 다음 ~” 라인은 이벤트가 발생한 후 예상되어야 하는 결과를 설명한다.

Strategic Design(전략적 디자인)

  • 전략적 디자인은 큰 디자인에 관한 것이며 큰 모델을 구성하는 많은 부분과 이러한 부분이 서로 어떻게 관련되는지에 초점을 맞추는 데 도움이 된다.
  • “내 모델은 돌에 던져졌다”라는 함정에 빠지지 않고 진행하기에 충분할 만큼 전면에 약간의 큰 디자인을 달성하는 데 도움이된다.
  • DDD에서 이러한 작은 모델은 경계 컨텍스트(Bounded Context)에 있다.
  • 경계 컨텍스트가 서로 관련되는 방식을 컨텍스트 매핑이라고 한다.

경계 컨텍스트(Bounded Context)

  • 각 모델에 대해 모델이 존재하는 컨텍스트를 의도적이고 명시적으로 정의한다.
  • 컨텍스트 생성에 대한 규칙은 없지만 모든 사람이 컨텍스트의 경계 조건을 이해하는 것이 중요하다.
  • 컨텍스트는 아래와 같은 방식으로 만들 수 있지만 아래의 방식이 전부는 아니다.
    • 팀 구성 방법
    • 코드 베이스의 구조와 레이아웃
    • 도메인의 특정 부분 내에서 사용
  • 컨텍스트 내부의 일관성과 통일성을 목표로 하고 컨텍스트 외부에서 모델이 사용되는 방식에 주의를 분산시키면 안된다.
  • 다른 컨텍스트에는 다른 개념을 가진 다른 모델이 있다.
  • 다른 컨텍스트에서 도메인의 유비쿼터스 언어의 다른 방언을 사용하는 것은 드문 일이 아니다.

컨텍스트 맵(Context Map)

  • 컨텍스트 매핑은 경계 컨텍스트 간의 접점과 변환이 명시적으로 매핑되는 설계 프로세스다.
  • 기존 환경을 매핑하는 데 집중하고 나중에 실제 변환을 처리한다.

  • 단일 경계 컨텍스트 내에서 지속적인 통합을 사용하여 서로 다른 이해에서 발생하는 파편을 유연하게 한다.
  • 빈번한 코드 병합, 자동화된 테스트 및 유비쿼터스 언어 적용은 제한된 컨텍스트 내부의 단편화를 빠르게 강조 표시한다.
  • Shared Kernel(공유 커널)
    • 다른 팀이 공유하기로 동의한 도메인의 하위 집합인 제한된 컨텍스트다.
    • 팀간의 좋은 의사 소통과 협업을 필요로한다.
    • 모든 것을 위한 공통 라이브러리가 아님을 기억해야 한다.
    • 공유 커널의 경우 설계 및 유지 관리가 어렵기 때문에 성숙한 팀에서 가장 효과적이다.

  • Customer/Supplier Development Teams(고객/공급자 개발팀)
    • 하나의 제한된 컨텍스트가 다른 제한된 컨텍스트를 제공하거나 공급할 때 다운스트림 컨텍스트는 업스트림 컨텍스트에 종속된다.
    • 어떤 컨텍스트가 업스트림이고 다운스트림인지 알면 공급자(업스트림)와 고객(다운스트림)의 역할이 명확해진다.
    • 두 팀은 인터페이스에 대한 승인 테스트를 공동으로 개발하고 이러한 테스트를 업스트림 경계 컨텍스트의 지속적인 통합에 추가해야 한다.
    • 이를 통해 고객팀은 비호환성에 대한 두려움 없이 개발을 계속할 수 있다는 확신을 갖게 된다.

  • Conformist(순응주의자, 준수자)
    • 다운스트림 컨텍스트로 작업하는 팀이 업스트림 컨텍스트에서 작업하는 팀과 협업할 영향력이나 기회가 없을 때 업스트림 컨텍스트를 따르는 것 외에는 선택의 여지가 거의 없다.
    • 업스트림 컨텍스트가 다운스트림 컨텍스트에 대한 인터페이스를 “지시”하는 데는 여러 가지 이유가 있을 수 있지만 순응 패턴으로 전환하면 많은 고통이 사라진다.
    • 단순히 업스트림 인터페이스를 준수함으로써 변경 불가능한 인터페이스를 변경하려는 복잡성보다 복잡성 감소가 더 큰 경우가 많다.
    • 일반적으로 다운스트림 모델의 품질은 업스트림 모델의 품질을 따르기 때문에, 업스트림 모델이 좋으면 다운스트림 모델도 좋고 업스트림 모델이 좋지 않으면 다운스트림 모델도 좋지 않다.
    • 그럼에도 불구하고 업스트림 모델은 다운스트림 요구 사항에 맞게 조정되지 않으므로 완벽하게 맞지 않는다.
  • Anti-corruption Layer(부패방지 계층)
    • 컨텍스트가 서로 다른 시스템에 존재하고 관계를 설정하려고 하면 한 모델이 다른 모델로 “유출”되는 결과가 발생하면 두 컨텍스트의 모델이 엉망으로 결합되어 둘 다 의도를 잃게 된다.
    • 이 경우 두 컨텍스트를 잘 분리하고 그 사이에 양방향 번역을 담당하는 격리 레이어를 도입하는 것이 좋다.
    • 이 부패방지 계층을 통해 고객은 자신의 모델 측면에서 작업할 수 있다.
    • 부패방지 계층은 레거시 시스템 또는 단계적으로 제거될 코드 기반을 처리하기 위한 훌륭한 패턴이다.

  • Separate Ways(별도의 방법)
    • 경계 컨텍스트 간의 매핑을 비판적으로 분석한다.
    • 필수 기능적 관계가 없으면 컨텍스트를 별도로 유지하는 것이 좋으며, 이유는 통합에 비용이 많이 들고 매우 낮은 수익을 얻을 수 있기 때문이다.
    • 이 패턴은 개발자(심지어 비즈니스 관리자)가 매우 제한된 범위 영역에서 고도로 집중된 솔루션을 찾을 수 있도록 하므로 상당한 복잡성을 제거한다.

Modeling the Domain(도메인 모델링)

  • 제한된 컨텍스트 내에서 노력은 표현력이 뛰어난 모델을 구축하는 데 집중된다.
  • 구현보다 의도를 더 많이 드러내는 모델이다.
  • 이것이 달성되면 도메인 표면의 개념과 모델이 유연하고 리팩토링이 더 간단해진다.

Dealing with Structure(구조 다루기)

  • Entity(엔티티)
    • 엔티티는 인스턴스가 전역적으로 식별 가능하고 평생 동일한 ID를 유지하는 클래스다.
    • 다른 속성의 상태는 변경될 수 있지만 ID는 변경되지 않는다.
    • 그림에서 주소는 여러 번 변경될 수 있지만 클라이언트의 ID는 변경되지 않는다.

  • Value Objects(값 객체)
    • 값 객체는 ID가 없는 가볍고 변경할 수 없는 객체이다.
    • 값이 더 중요하지만 단순한 데이터 전송 대상은 아니다.
    • 값 객체는 엔티티에서 무거운 연산을 오프로드하여 복잡한 계산을 수행하기에 좋다.
    • 구성하기가 훨씬 쉽고 안전하며 엔티티의 무거운 연산을 오프로드하여 엔티티가 생명 주기 추적기의 역할에 집중할 수 있도록 도와준다.
    • 값 객체는 수명 주기가 간단하고 모델을 크게 단순화할 수 있다.
    • 정적 형식 언어에 대해 컴파일 시간에 형식 안정성을 도입하는 데 적합하다.
    • 값 객체의 메서드는 사이드 이펙로부터 자유로워야 하기 때문에 함수형 프로그래밍과 유사한 부분이 있다.
    • 그림에서 클라이언트의 주소가 변경되면 새 주소 값 객체가 인스턴스화되어 클라이언트에 할당된다.

  • Cardinality of Associations(연결의 카디널리티)
    • 클래스 간 연관의 카디널리티가 클수록 구조가 더 복잡해진다.
    • 예선을 추가해 더 낮은 카디널리티를 목표로 한다.
    • 양방햔 연결은 복잡성을 유발하기 때문에 두 객체 사이에 양방향으로 탐색할 수 있는 것이 꼭 필요한 것인지 비판적으로 확인할 필요가 있다.
    • 그림에서 모든 Project에 대해 Person 객체를 요청할 필요가 거의 없지만 ProjectRole에 대해 항상 Project 객체를 요청하는 경우 연결을 단 방향으로 만들 수 있다.
    • 방향은 메모리에 있는 모델의 객체 연관을 존중하는 것이다.
    • Person 객체에 대한 모든 Project 객체를 찾아야 하는 경우 저장소에서 쿼리를 사용하여 Person에 대한 모든 Project를 찾을 수 있다.
  • Services(서비스)
    • 엔티티 또는 값 객체와 같은 단일 클래스에 동작을 할당하는 것이 불가능한 서비스가 있다.
    • 이는 하나의 클래스가 동작에 대해 책임을 지지않고 여러 클래스에서 작동하는 순수 기능의 경우다.
    • 이러한 경우 서비스 클래스라고 하는 Stateless 클래스가 이 동작을 캡슐화하기 위해 도입된다.
  • Aggregates(집계)
    • 모델에 더 많은 것을 추가하면 객체 그래프가 커지고 복잡해질 수 있다.
    • 큰 객체 그래프는 트랜잭션 경계, 배포 및 동시성과 같은 기술 구현을 매우 어렵게 만든다.
    • Aggregate는 경계 내부의 클래스가 객체 그래프의 나머지 부분과 “연결 해제”되도록 하는 일관성 경계이다.
    • 각 Aggregate에는 Aggregate의 “루트” 역할을 하는 하나의 엔티티가 있다.
    • Aggregate를 생성할 때 Aggregate가 여전히 도메인에서 의미있는 단위로 처리되는지 확인해야 하고, “삭제” 테스트를 적용하여 집계 경계의 정확성을 테스트한다.
    • 삭제 테스트에서 루트가 삭제된 경우 Aggregate 내(및 Aggregate 외부)도 삭제될 객체를 비판적으로 확인한다.
    • Aggregate는 아래와 같은 규칙을 따라야 한다.
      • 루트는 글로벌 아이덴티티를 가지고 나머지 로컬 아이덴티티를 가진다.
      • 루트는 모든 불변량이 충족되는지 확인한다.
      • Aggregate 외부의 엔티티는 루트에 대한 참조만 보유한다.
      • 삭제는 Aggregate의 모든 항목을 제거한다.
      • 객체가 변경되면 모든 불변량이 충족되어야 한다.
    • Aggregate는 도메인 단순화 및 기술 개선이라는 두 가지 목적을 수행한다는 점을 기억해야 한다.
    • Aggregate간에 불일치가 있을 수 있지만 모든 Aggregate는 결국 서로 일치한다.

Dealing with Life Cycles(수명 주기 다루기)

  • Factories(공장)
    • 팩토리는 일부 Aggregate의 수명 주기 시작을 관리한다.
    • 이것은 GoF 팩토리 또는 빌더 패턴의 애플리케이션이다.
    • Aggregate의 규칙, 특히 Aggregate내의 불변이 존중되도록 주의해야 한다.
    • 팩토리는 때때로 매우 유용하지만 필수적인 것은 아님을 기억해야 한다.

  • Repositories(저장소)
    • 팩토리가 수명 주기를 관리하는 동안 저장소는 수명 주기의 중간과 끝을 관리한다.
    • 저장소는 객체 검색을 위해 객체 관계형 매퍼에게 지속성 책임을 위임할 수 있다.
    • 저장소도 Aggregate와 함께 작동한다는 점을 기억해야 한다.
    • 검색된 객체는 Aggregate 규칙을 준수해야 한다.


Dealing with Behavior(행동 다루기)

  • Specification Pattern(사양 패턴)

    • 규칙, 검증 및 선택 기준을 모델링해야 하는 경우 사양 패턴을 사용한다.
    • 사양 구현은 객체가 사양의 모든 규칙을 충족하는지 여부를 테스트한다.
    • class Project { public boolean isOverdue() { ... } public boolean isUnderbudget() { ... } }
    • 연체 및 예산 부족 프로젝트에 대한 사양은 프로젝트에서 분리되어 다른 클래스의 책임이 될 수 있다.
    • public interface ProjectSpecification { boolean isSatisfiedBy(Project project); } public class ProjectIsOverdueSpefication implements ProjectSpecification { public boolean isSatisfiedBy(Project project) { ... } }
    • 이것은 클라이언트의 코드를 더 읽기 쉽고 유연하게 만들어준다.
    • If (projectIsOverdueSpecification.isSatisfiedBy(theCurrentProject) { ... }
  • Strategy Pattern(전략 패턴)

    • 정책(Policy) 패턴이라고도 하는 전략 패턴은 알고리즘을 교환 가능하게 만드는 데 사용된다. 이 패턴은 다양한 “부분”에서 제외된다.

    • 두 가지 계산을 기반으로 프로젝트의 성공을 결정하는 예시를 고려해야 한다.

      1. 프로젝트가 제 시간에 완료되면 성공한 것이다.
      2. 예산을 초과하지 않으면 프로젝트가 성공한 것이다.
      public class Project {
        boolean isSuccessfullByTime();
        boolean isSuccessfullByBudget();
      }
    • 전략 패턴을 적용하여 두 가지 다른 계산에 대한 알고리즘을 포함하는 정책 구현 클래스에 특정 계산을 캡슐화할 수 있다.

    • public interface ProjectSuccessPolicy { boolean isSuccessful(Project project); } class SuccessByTime implements ProjectSuccessPolicy { ... } class SuccessByBudget implements ProjectSuccessPolicy { ... }

    • 정책을 사용하도록 원래 프로젝트 클래스를 리팩토링하여 프로젝트 클래스 자체가 아니라 정책 구현의 성공 기준을 캡슐화한다.

    • class Project { boolean isSuccessfule(ProjectSuccessPolicy policy) { return policy.isSuccessful(this); } }

  • Composite Pattern(복합 패턴)

    • 복합 패턴은 모델링되는 도메인 내에서 GoF 패턴을 직접 적용한 것이다.
    • 기억해야 할 중요한 점은 클라이언트 코드가 복합 요소를 나타내는 추상 유형만 처리해야 한다는 점이다.
    • public class Project { private List<Milestone> milestones; private List<Task> tasks; private List<Subproject> subprojects; }
    • SubprojectMilestonTask가 있는 프로젝트다.
    • Milestone은 기한이 있지만 기간이 없는 Task이다.
    • 복햅 패턴을 적용하면 다른 구현으로 새로운 유형의 활동을 도입할 수 있다.
    • interface Activity { Date due(); } public class Subproject implements Activity { private List<Activity> activities; public Date due() { ... } } public class Milestone implements Activity { public Date due() { ... } } public class Task implements Activity { public Date due() { ... } public int duration() { ... } }
    • 이제 Project의 모델은 훨씬 간단해진다.
    • public class Project { private List<Activity> activities; }
    • 모델을 UML 표현으로 그리면 아래와 같다.

Application Architecture(애플리케이션 아키텍처)

  • “디자인의 초점”이 다양한 동작을 하는 도메인 모델을 만드는 것이라면 “도메인이 참여하는 아키텍처”는 모델을 인프라스트럭처 없이 유지하는데 기여해야 한다.
  • 일반적으로 계층 구조를 사용하여 시스템의 다른 부분에서 도메인을 격리할 수 있다.
  • 각 레이어는 그 아래에 있는 레이어만 인식한다. 따라서 하위 수준의 계층은 상위 계층에 호출을 할 수 없다.
  • 또한 각 계층은 매우 응집력이 있으며 특정 계층에 있는 클래스는 계층의 목적과 책임을 존중하는 데 세심한 주의를 기울인다.
User Interface 사용자 인터페이스를 구성하고 도메인 모델과의 상호 작용을 관리하는 일을 담당한다. 일반적인 구현 패턴은 모델-뷰-컨트롤러다.
Application 뷰가 도메인과 공동 작업할 수 있도록 하는 얇은 계층이다. 주의: 변위된 도메인 동작에 대한 쉬운 "덤프 그라운드"이며 "트랜잭션 스크립트" 스타일 코드의 자석이 될 수 있다.
Domain 행동이 풍부하고 표현력이 풍부한 도메인 모델이다. 리포지토리와 팩토리는 도메인의 일부다. 그러나 리포지토리가 위임할 수 있는 객체 관리형 매퍼는 인프라스트럭처 계층의 일부다.
Infrastructure 기술 관련 결정을 다루고 의도보다는 구현에 더 중점을 둔다. 이 계층에서 도메인 인스턴스를 생성할 수 있지만 일반적으로 이러한 객체에 대한 참조를 얻기 위해 이 계층과 상호작용하는 저장소다.
  • 인터페이스로 레이어를 디자인하는 것을 목표로 하고 레이어 간의 “통신”을 위해 이러한 인터페이스를 사용한다.
  • 또한 도메인 계층을 사용하는 코드가 트랜잭션 경계를 제어하도록 해야 한다.

Recently Added Patterns(최근에 추가된 패턴)

Big Ball of Mud

  • 혼합된 여러 개념적 모델로 구성된 기존 시스템을 다루기 위한 전략적 설계 패턴이며, 우연한 또는 우연한 종속 논리와 함께 유지된다.
  • 이러한 경우에는 혼잡함에 경계선을 그어주고 이 맥락에서 정교한 모델링을 시도해서는 안된다.
  • 이 컨텍스트가 다른 컨텍스트로 확장되는 것을 주의해야 한다.

Domain Events

  • 때때로 도메인 전문가는 도메인의 변경을 일으키는 실제 이벤트를 추적하기를 원한다.
  • 도메인 이벤트를 소프트웨어 자체의 일부인 시스템 이벤트와 혼용해서는 안된다.
  • 도메인 이벤트에는 이벤트에 대한 정보를 시스템으로 전달하는 데 사용되는 해당 시스템 이벤트가 있을 수 있지만 도메인 이벤트는 도메인 모델의 완전한 부분이다.
  • 도메인 이벤트 집합에서 엔티티 상태를 추론할 수 있도록 이러한 이벤트를 도메인 객체로 모델링한다.
  • 이벤트 객체는 과거에 무언가를 모델링하기 때문에 일반적으로 변경할 수 없다.
  • 일반적으로 이러한 객체에는 타임스탬프, 이벤트 설명 및 필요한 경우 도메인 이벤트 자체에 대한 일부 ID가 포함된다.
  • 분산 시스템에서 도메인 이벤트는 모든 노드에서 비동기적으로 발생할 수 있으므로 특히 유용하다.
  • 엔티티의 상태는 전체 시스템의 완전한 정보 세트에 의존할 필요없이 현재 노드에 알려진 이벤트에서 추론할 수도 있다.

참고 자료