• 모델링 과정을 매우 복잡한 도메인까지 확장하는 원리
  • 성공적인 모델 : 규모와는 상관없이 모순되거나 정의가 겹치지 않고 처음부터 끝까지 논리적인 일관성 유지
  • Bounded Context 
  • Distillation
  • 대규모 구조 : 전혀 공통점이 없는 부분들이 맞물려 돌아가도록 일관성을 가져다 줌
    • Responsibility Layer
    • Evolving Order(발전하는 질서)
모델의 무결성 유지
  • 각 모델이 적용되는 경계를 분명하게 합의하는 것에서 출발
  • 단일화(unification) : 각 용어가 모호하지 않고 모순되는 규칙이 없는 모델의 내적 일관성
  • 다른 여러 모델 간의 경계와 관계를 표시해줄 수단 필요
    • Context Map : 프로젝트 분야를 매핑, 각 컨텍스트 간의 관계의 전체적인 개관
    • Bounded Context : 각 모델의 적용가능성의 범위 정의
    • Continuous Integration : 프로세스를 토대로 모델의 단일화 유지
    • Shared Kernel
    • Separate Ways
Bounded Context
  • 규모가 큰 프로젝트에는 다수의 모델이 공존하며, 서로 다른 모델은 서로 다른 컨텍스트에 적용된다
  • 한 팀 내에서도 다수의 모델이 공존할 수 있다
  • 다수의 모델 탓에 발생하는 문제의 해결 방법
    • 소프트웨어 내의 제한된 부분으로 특정 모델의 범위를 명확하게 정의
    • 팀의 구성과 조화
    • 컨텍스트의 경계를 팀 조직, 애플리케이션의 특정 부분에서의 사용법, 코드 기반이나 데이터베이스스키마와 같은 물리적인 형태의 관점에서 명시적으로 설정
    • 경계 내에서는 모델을 엄격하게 일관된 상태로 유지하고 경계 바깥의 이슈 때문에 초점이 흐려지거나 혼란스러워져서는 안된다
    • 모델을 책임지는 팀에서는 영속화를 비롯한 각 객체의 전체 생명주기를 다룬다

Bounded Context안의 균열 인식

  • 코드로 작성된 인터페이스가 서로 맞지 않는 경우
  • 예상치 못한 행위
  • 언어를 혼동한 상태로 구사
  • 중복개념 : 실제로 같은 개념을 나타내는 두개의 모델 요소가 존재하는 것
  • 허위동족언어(false cognates) : 같은 용어를 사용하는 두 사람이 서로 같은 것을 이야기하고 있다고 생각하지만 실제로는 그렇지 않은 경우, 개념상의 충돌
  • 해결방안
    • Continuous Integration
    • Context Map
    • Shared Kernel
    • Customer/Supplier Development Team
    • Conformist
    • Anticorruption Layer
    • Separate Ways
    • Open Host Service
    • Published Language

Continuous Integration

  • 내부적으로 균열이 발생할 때 이를 빠르게 포착하고 정정할 수 있을 정도로 컨텍스트 내의 모든 작업을 빈번하게 병합해서 일관성 유지
  • 모델 개념의 통합
    • 팀 구성원 간의 부단한 의사소통을 토대로 통합
  • 구현 수준에서의 통합
    • 모델내의 균열을 조기에 드러내는 체계적인 병합/빌드/테스트 프로세스를 토대로 통합
  • 프로세스
    • 단계적이고 재생 가능한 병합/빌드 기법    
    • 자동화된 테스트 스위트
    • 수정사항이 통합되지 않은 상태로 존재할 수 있는 시간을 적당히 짧게 유지하는 규칙

Context Map

  • Bounded Context간에 코드를 재사용하는 것음 위험하므로 기능과 데이터는 번역 과정을 거쳐 통합해야 한다.
  • 서로다른 컨텍스트간의 관계를 정의하고 프로젝트상의 모든 모델 컨텍스트를 아우르는 전체적인 뷰를 만들면 혼란을 줄일 수 있다
  • Context Map은 프로젝트 관리와 소프트웨어 설계 영역 사이에 걸쳐있는 개념
  • 의사소통을 위해 컨텍스트 간의 번역에 대한 윤곽을 명확하게 표현
  • 컨텍스트 간에 공유해야 하는 정보를 강조
  • 모델과 모델이 만나는 경계 지점 서술
  • Bounded Context간의 관계 패턴
    • Shared Kernel
    • Customer/Supplier(현재 가장 많이 적용되어 있는 패턴)
    • Separate Ways
    • Open Host Service
    • Anticorruption Layer(wrapper, translator, facade, service)

디스틸레이션

  • 혼합된 요소를 분리해서 본질을 좀더 값지고 유용한 형태로 뽑아내는 과정
  • Generic subdomain - 모델에서 가장 특색이 없는 측면 제거
  • Coherent Mechanism - 용도가 다양하고, 의미전달이 용이하며, 유연한 설계를 통해 캡슐화
  • Segregated Core
  • Abstract Core - 가장 근본적인 개념과 관계를 순수한 형태로 표현
  • Core Domain
    • Domain Vision Statement - 최소한의 투자로 기본개념과 가치를 
    • Highlighted Core - 의사소통을 향상시키고 의사결정에 도움

Core Domain

  • 도메인 모델을 가치있는 자산으로 만들려면 모델의 핵심적인 측면을 다루기 수월해야 하고 애플리케이션의 기능성을 만들어내는 데 충분히 활용할 수 있어야 한다
  • Core Domain은 시스템에서 가장 큰 가치가 더해지는 곳이다
  • 시스템의 비전을 수행하기에 충분한 심층 모델을 찾고 유연한 설계를 개발할 수 있게 코어에 노력을 쏟아라

Generic Subdomain

  • 모델은 일반 원칙(누구나 알고 있는것)이나 세부사항(보조적인 역할을 수행하는 전문 분야에 속하는 것) 탓에 정체된다
  • 개발 시 선택사항
    • 기성 솔루션(상용 또는 오픈소스)
    • 공표된 설계나 모델
    • 외주 제작
    • 사내구현
  • 일반화가 재사용 가능하다는 의미는 아니다
  • 필요한 만큼만 투자
  • 쉽기 때문에 보조 시스템을 먼저 구축할 경우 위험관리의 목적을 헛되게 할 수 있음

Domain vision Statement

  • 도메인 모델의 본질과 해당 도메인 모델이 얼마나 기업에 가치 있는가에 초점을 맞춘다
  • 팀 내에서 방향성을 공유하게 해 줌
  • 작성방법
    • Core Domain을 짧게 기술하고 그것이 가져올 가치에 해당하는 가치제안을 작성
    • 이 도메인 모델과 다른것과 구별하는데 도움되지 않는 측면은 무시
    • 도메인 모델이 어떻게 다양한 관심사를 충족하고 균형을 이루는지 보여라
    • 한정된 범위에서 내용을 유지하라
    • 초기에 이 선언문을 작성하고 새로운 통찰력을 얻을 때마다 선언문을 개정하라


Highlighted Core

  • 어떤 모델에서 특별히 중요한 부분을 그것을 구체화한 구현과 함께 표시할 필요가 있는데, 이러한 표시는 모델에 대한 설명이지 반드시 모델 자체의 한 부분일 필요는 없다
  • 기법
    • 디스틸레이션 문서 : Core Domain과 Core의 구성요소 사이에서 일어나는 상호작용을 기술하는(3에서 7페이지 가량의) 매우 간결한 문서를 작성하라
    • 표시된 Core : 모델의 주요 저장소 안에 있는 Core Domain의 구성요소에 대해 그것의 역할을 설명하려하지 말고 표시하라. 개발자가 힘들이지 않고도 Core의 안과 밖을 알 수 있게 하라

Cohesive Mechanism

  • 의도를 드러내는 이름이 이정된 메서드에 복잡한 알고리즘을 숨기면 "무엇"이 "어떻게"와 분리된다
  • 문제해결을 위한 알고리즘을 제공하는 수많은 메서드가 문제를 표현하는 메서드를 분분명하게 만드는 경우
    • 개념적으로 Cohesive Mechanism을 별도의 경량 프레임워크로 분할
    • Intention-Revealing Interface로 프레임워크의 기능을 노출
    • 해법의 복잡성("어떻게")을 프레임워크에 위임해서 문제("무엇")을 표현하는데 집중    

Segregated Core

  • 설계자가 가장 중요한 관계를 분명하게 볼 수 없다면 취약한 설계로 이어지는 결과가 나타난다
  • 모든 일반적이거나 보조적인 역할을 하는 구성요소를 다른 객체로 추출해서 다른 패키지에 배치하라
Abstract Core
  • 모델의 가장 근본적인 개념을 식별해서 그것을 별도의 클래스나 추상 클래스, 또는 인터페이스로 추출하라
  • 이 추상 모델이 중요 컴포넌트 간에 발생하는 상호작용을 대부분 표현할 수 있게끔 설계하라
  • 특화되고 세부적인 구현클래스는 하위 도메인을 기준으로 정의된 자체적인 모듈에 남겨둔 상태에서 이 추상적이면서 전체적인 모델을 자체적인 모듈에 배치하라

리팩토링의 대상선택

  • 문제의 근원에 Core Domain이나, Core 와 지원요소와의 관계가 있는지 살핀다. 그렇다면 가장먼저 고쳐야 한다
  • 마음껏 리팩토링할 수 있는 상황이라면 제일먼저 Core Domain을 더 잘 분해하고 Core의 격리를 개선하며, 보조적인 하위 도메인이 Generic하게 만드는 데 집중한다.


리팩터링은 엔트로피와의 싸움이며 레거시 시스템이 퇴보하는 것을 막는 최전선에 놓여 있다.


암시적인 개념을 명확하게

  • 도메인 전문가가 사용하는 언어에 귀 기울여라
    • 어색한 부분을 조사하라
    • 설명하기 힘들만큼 복잡한 작업을 수행하는 프로시저와 관련된 부분이나 새로운 요구사항 탓에 복잡성이 증가하는 부분
    • 모순점에 대해 깊이 고민하라
  • 서적을 참고하라
  • 시도하고 또 시도하라
    • 모델러/설계자는 자신의 아이디어에 집착해서는 안된다
다소 불명확한 개념을 모델링하는 법
1. 명시적인 제약조건
  • 제약조건을 별도의 메서드로 분리하고 제약조건의 의미를 분명하고 명확하게 표현할 수 있게 메서드의 이름을 짓는다
  • 부여된 이름을 사용해서 제약조건에 관한 토의가 가능
  • 잘못된 제약조건 설계의 식별
    • 제약조건을 평가하려면 해당 객체의 정의에 적합하지 않은 데이터가 필요하다
    • 관련된 규칙이 여러 객체에 걸쳐 나타나며, 동일한 계층구조에 속하지 않는 객체 간에 중복 또는 상속 관계를 강요한다
    • 설계와 요구사항에 관한 다양한 논의는 제약조건에 초점을 맞춰 이뤄지지만 정작 구현단계에서는 절차적인 코드에 묻혀 명시적으로 표현되지 않는다.
2. 도메인 객체로서의 프로세스
  • 객체는 절차를 캡슐화해서 절차 대신 객체의 목표 의도에 관해 생각하게 해야 한다
  • 어떤 프로세스를 선택할 것인가는 곧 어떤 객체를 선택할 것인가가 되고, 각 객체는 각기 다른 Strategy를 표현한다

3. 명세(Specification)

  • 어떤 객체가 특정기준을 만족하는지 판단하는 술어다.
  • 다른객체에 대한 제약조건을 기술하며, 제약조건은 존재할 수도 존재하지 않을 수도 있다
  • 특별한 목적을 위해 술어와 유사한 명시적인 Value Object를 만들어라

유연한 설계

  • 너무 과도한 추상 계층과 간접 계층이 존재하면 오히려 유연성에 방해가 된다
  • 정교한 시스템을 목적으로 조립가능하고 이해하기 어렵지 않은 요소를 만들어내려면 적당한 수준의 엄밀한 설계형식과 접목하고자 노력해야 한다

1. 의도를 드러내는 인터페이스(Intention-Revealing Interface)

  • 캡슐화로 클라이언트 코드는 단순해지고 상위 수준의 개념 관점에서 코드를 이해할 수 있다
  • 인터페이스를 구성하는 각 요소(타입, 메서드, 인자 이름)의 이름을 토대로 설계 의도를 드러낸다
  • 결과와 목적만을 표현하도록 클래스와 연산의 이름을 부여(Ubiquitous Language 기반)
  • 행위에 대한 테스트를 먼저 작성
  • 방법이 아닌 의도를 표현하는 추상적인 인터페이스위로 모든 까다로운 메커니즘을 캡슐화
2. 부수효과가 없는 함수(Side-Effect-Free-Function)
  • 부수효과를 일으키지 않으면서 결과를 반환하는 연산을 함수라 한다
  • 함수는 여러번 호출해도 무방하며 매번 동일한 값을 반환한다
  • 명령과 질의를 엄격하게 분리 - 변경을 발생시키는 메서드는 도메인 데이터를 반환하지 않아야하고 가능한 단순하게 유지해야 한다
  • 명령과 질의를 분리하는 대신 연산의 결과를 표현하는 새로운 Value Object를 생성해서 반환
  • 상태변경을 수반하는 로직과 계산이 혼합된 연산은 리팩터링을 거쳐 두개의 연산으로 분리해야 한다
  • 복잡한 계산을 처리하는 책임을 Value Object로 옮긴다

3. 단언(Assertion)

  • 연산의 사후 조건과 클래스 및 Aggregate의 불변식을 명시하라
  • 개발자들이 의도된 단언을 추측할 수 있게 인도하고, 쉽게 배울수 있고 모순된 코드를 작성하는 위험을 줄이는 응집도 높은 개념이 포함된 모델을 만들려고 노력하라
4. 개념적 윤곽(Conceptuel Contour)
  • 이 개념이 현재 모델과 코드에 포함된 관계를 기준으로 했을때 적절한가, 또는 현재 기반을 이루는 도메인과 유사한 윤곽을 나타내는가?
  • 변경되는 부분과 변경되지 않는 부분을 나누는 중심 축을 식별하고, 변경을 분리하기 위한 패턴을 명확하게 표현하는 개념적 윤곽을 찾아라
  • 객체와 메서드를 와해시킬 정도로 광범위한 변경을 야기하는 요구사항이 나타났다는 것은 도메인에 관해 알고 있는 지식을 개선해야 한다는 메시지다
4. 독립형 클래스(Standalone Class)
  • Module과 Aggregate 모두 지나치게 얽히고 설키는 상호의존성을 방지하는 것이 목적이다
  • 명시적인 참조에 비해 암시적인 개념이 훨씬 더 많은 정신적 과부하를 초래한다.
  • 현재 상황과 무관한 모든 개념을 제거하라
  • 클래스가 완전히 독립적으로 바뀌고 단독으로 검토하고 이해할 수 있을 것이다
  • 모든 비본질적인 의존성을 제거하는 것이 목표다
  • 가장 복잡다단한 계산을 Standalone class로 도출하려고 노력하라
  • Standalone Class는 극단적으로 결합도를 낮춘 것이다

5. 연산의 닫힘(Closure of Operation)

  • 구현자(implementer)가 연산에 사용되는 상태를 포함하고 있다면 연산의 인자로 구현자를 사용하는 것이 효과적이므로 인자의 타입과 반환 타입을 구현자의 타입과 동일하게 정의한다. 이런 방식으로 정의된 연산은 해당 타입의 ㅇ니스턴스 집합에 닫혀 있다. 닫힌 연산은 부차적인 개념을 사용하지 않고도 고수준의 인터페이스를 제공한다.
  • 일반적으로 Entity는 어떤 계산의 수행결과를 표현하는 개념이 아니다. 따라서 대부분의 경우 Value Object에서 이 패턴을 적용할 기회를 찾을 수 있다.
6. 선언적 설계
  • 일반적으로 일종의 실행 가능한 명세(executable specification)로서 프로그램 전체 혹은 프로그램의 일부를 작성하는 방식
  • 특성(properties)을 매우 정확하게 기술함으로써 소프트웨어를 제어하는 것
  • 선언적인 프로그램의 이점을 누리려면 모든 개발자가 프레임워크의 규칙을 준수 해야 한다
  • 아주 좁은 범위로 한정된 프레임워크를 사용해서 영속성과 객체관계형 매핑과 같이 매우 지루하고 오류가 발생하기 쉬운 설계 측면을 자동화한 경우에 큰 가치를 얻었다
  • 규칙기반(rule base)
    • 원칙적으로는 선언적이지만 대부분의 시스템은 성능 최적화를 위해 제어술어(control predicate)를 포함
    • 이러한 제어코드는 부수효과를 수반하므로 더는 선언된 규칙만으로는 완전한 행위를 지시할 수 없다
7. 도메인 특화 언어
  • 특정 도메인을 위해 구축된 특정 모델에 맞게 조정된 프로그래밍 언어를 사용해 클라이언트 코드 작성
  • 프로그램의 표현력을 월등히 향상시킬 수 있음
  • Ubiquitous Language와도 가장 높은 일관성을 유지 할 수 있음
8. 하위 도메인으로 분할하라
  • 조금씩 뜯어내어 접근
  • 모델의 일부 영역이 전문적인 영역이거나, 상태변경에 제약을 가하는 복잡한 규칙을 적용한다면 끄집어내어 별도의 모델이나 규칙을 선언적으로 표현해주는 간단한 프레임워크 내부로 옮긴다
  • 하나의 영역에 집중

9. 가능하다면 정립된 정형화를 활용하라

  • 오랜 시간 동안 정립되어 온 개념적인 체계 이용
  • 수학적인 개념 - 도메인에 적절히 특화된 수학은 깔끔한 동시에 명확한 규칙과 결합할 수 있어서 사람들이 이해하기도 쉽다
모델과 디자인 패턴의 연결
  • 기술저인 문제에 대한 해법뿐 아니라 개념적인 도메인에 관한 해법도 제공해야 한다는 것이 디자인 패턴을 도메인 패턴으로 적용하기 위한 유일한 요구사항이다
1. Strategy(전략, 정책 패턴)
  • 여러프로세스의 선택이라는 문제를 해결
  • 프로세스의 규칙과 프로세스를 제어하는 행위를 서로 분리
  • 도메인 패턴으로 사용하는 관점에서는 프로세스 또는 정책적인 규칙과 같은 하나의 개념을 표현하는 능력에 중점

2. Composite

  • Composite내부에 포함된 모든 구성요소를 포괄하는 추상 타입을 정의
  • 계층구조상의 모든 수준에서 동일한 행위를 제공
  • 크고 작은 각 부분이 전체 구조를 투명하게 반영


Layered Architecture

  • SoC를 아키텍처 레벨에서 가능하게 해주는 패턴
  • 계층을 나누어 각 계층의 관심사에 집중할 수 있도록 한다.
  • 일반적으로 사용자 인터페이스, 응용, 도메인, 인프라스트럭쳐 계층이 있다.


도메인 모델 빌딩블럭

엔티티(Entity)

  • 어떤 객체를 일차적으로 해당 객체의 식별성으로 정의
  • 객체의 생명주기 내내 이어지는 추상적인 연속성
  • 엔티티의 속성 보다는 정체성에 초점


Value Object

  • 개념적 식별성을 갖지 않으면서 도메인의 서술적 측면을 나타내는 객체
  • 모델에 포함된 어떤 요소의 속성에만 관심
  • 불변적
  • 객체의 수가 많아질 수 있으므로 객체를 고유하여 최적화 가능


Service

  • 모델에서 독립적인 인터페이스로 제공되는 연산
  • 다른 객체와의 관계를 강조
  • 연산의 명칭은 Ubiquitous language에 도입되어야 함
  • 매개변수와 결과는 도메인 객체여야 함
  • 도메인 계층에서만 이용되는 것은 아님
  • 각 계층의 서비스와 도메인 서비스를 적절하게 나누는 것이 중요 함


Module

  • 패키지
  • 일련의 응집력있는 개념
  • 모듈의 이름은 도메인에 통찰력을 줄 수 있어야 함


도메인 객체의 생명주기


도메인객체의 관리 문제

  • 생명주기 동안의 무결성 유지하기
  • 생명주기 관리의 복잡성으로 모델이 난해해지는 것을 방지하기

해결방법

Aggregate

  • 소유권과 경계를 명확히 정의하여 객체 간의 연관관계가 복잡해지지 않도록 한다
  • 도메인 객체의 무결성 유지에 중요 함
  • 생명주기의 전 단계에서 불변식이 유지돼야 할 범위를 표시

Factory

  • 생명주기의 초기단계
  • 복잡한 객체와 Aggregate를 생성하고 재구성

Repository

  • 생명주기의 중간과 마지막
  • 영속 객체를 찾아 조회하는 수단
Aggregate
  • root와 boundary
  • boundary : 무엇이 포함되고 무엇이 포함되지 않는지 정의
  • root : 단 하나만 존재, 특정 엔티티
  • 경계 바깥의 객체는 해당 Aggregate의 구성요소 가운데 root 만 참조 가능
  • 불변식 : 데이터가 변경될 때마다 유지돼야 하는 일관성 규칙
  • 구현 규칙
    • 루트 엔티티는 전역 식별성을 지니며 불변식을 검사할 책임이 있다
    • 루트는 Value Object의 복사본을 단른 객체에 전달해 줄 수 있다
    • 삭제 연산은 경계안의 모든 요소를 한번에 제거해야 한다.
    • 변경시 전체 불변식은 반드시 지켜져야 한다




전역식별성

지역식별성

모델

  1. 중요한 사실이나 사상의 일부 측면을 나타냄 
  2. 대상을 단순화 한것
  3. 당면한 문제와 관련된 것을 추상화

도메인

  • 사용자가 프로그램을 사용하는 대상 영역
  • 컴퓨터와 거의 관련이 없음
도메인 모델
  • 특정한 다이어 그램이 아니라 전달하고자 하는 아이디어
  • 지식을 엄격하게 구성하고 선택적으로 추상화한 것
  • 모델과 핵심 설계는 서로 영향을 주며 구체화 된다
  • 모델은 모든 팀 구성원이 사용하는 언어의 중추
  • 모델은 지식의 정수만을 뽑아낸 것이다
Ubiquitous language
  • 클래스, 주요 연산등의 이름
  • 명시적으로 드러나는 규칙을 토론하기 위한 용어
  • 도메인 모델에 적용하는 패턴의 이름
  • 공통언어(도메인 전문가와 개발팀간)로서 모델과 코드에서 사용해야 한다.
  • 모델, 설계, 구현 전체를 연결하는 핵심 키

도메인 모델 작성시 가장 중요하다고 생각하는 것은 추상화이다. 특히 기존 코드를 분석하여 모델을 만드는 경우 코드의 세세한 부분까지 모델에 표현하려는 욕심은 자제해야 한다.



+ Recent posts