Programming/MSA

6. The Life Cycle of a Domain Object

armyost 2022. 7. 22. 16:43
728x90


모든 개체에는 수명 주기가 있습니다. 객체는 생성되고 다양한 상태를 거치며 결국 아카이브되거나 삭제되어 죽습니다. 물론 이들 중 다수는 생성자를 쉽게 호출하여 생성되고 일부 계산에 사용된 다음 가비지 수집기에 버려지는 단순하고 일시적인 개체입니다. 그러한 개체를 복잡하게 만들 필요가 없습니다. 그러나 다른 개체는 수명이 더 길며 모든 개체가 활성 메모리에서 소비되는 것은 아닙니다. 그들은 다른 객체와 복잡한 상호 의존성을 가지고 있습니다. 그것들은 불변이 적용되는 상태 변경을 거칩니다. 이러한 개체를 관리하는 것은 MODEL-DRIVEN DESIGN 시도를 쉽게 탈선시킬 수 있는 Challenge를 야기합니다.

Challenge는 두 가지 범주로 나뉩니다. 
1. 라이프 사이클 전반에 걸쳐 무결성 유지
2. 모델이 라이프 사이클 관리의 복잡성으로 인해 휩쓸리는 것을 방지

이 장에서는 세 가지 패턴을 통해 이러한 문제를 다룰 것입니다. 
첫째, AGREGATES는 객체의 혼란스럽고 얽힌 웹을 피하고 명확한 소유권과 경계를 정의하여 모델 자체를 강화합니다. 이 패턴은 수명 주기의 모든 단계에서 무결성을 유지하는 데 중요합니다.

다음으로, FACTORIES를 사용하여 복잡한 객체와 AGREGATES를 생성 및 재구성하고 내부 구조를 캡슐화한 상태로 유지하면서 수명 주기의 시작 부분에 초점을 맞춥니다. 

마지막으로, REPOSITORIES는 관련된 막대한 인프라를 캡슐화하는 동시에 지속적인 객체를 찾고 검색하는 수단을 제공하여 수명 주기의 중간과 끝을 처리합니다. REPOSITORIES와 FACTORIES는 그 자체가 도메인에서 온 것은 아니지만 도메인 설계에서 의미 있는 역할을 합니다. 이러한 구성은 모델 개체에 대한 액세스 가능한 핸들을 제공하여 MODEL-DRIVEN DESIGN을 완성합니다.

- Aggregates : 관련성 있는 객체들을 그루핑한것
- Factories : 새로운 객체를 생성하는 역할
- Repository : 필요한 객체를 찾기 위한 진입점

AGREGATES를 모델링하고 FACTORIES 및 REPOSITORIES를 설계에 추가하면 모델 객체를 라이프 사이클 전반에 걸쳐 체계적이고 의미 있는 단위로 조작할 수 있습니다. AGREGATES는 수명 주기의 모든 단계에서 불변량이 유지되어야 하는 범위를 표시합니다. FACTORIES 및 REPOSITORIES는 특정 라이프 사이클 전환의 복잡성을 캡슐화하는 AGREGATES에서 작동합니다. 

결합의 미니멀리즘 디자인은 순회를 단순화하고 관계의 폭발을 어느 정도 제한하는 데 도움이 되지만 대부분의 비즈니스 도메인은 너무 상호 연결되어 결국 객체 참조를 통해 길고 깊은 경로를 추적하게 됩니다. 어떤 면에서 이 얽힌 것은 세상의 현실을 반영하며, 우리에게 날카로운 경계를 강요하는 경우는 거의 없습니다. 소프트웨어 설계의 문제입니다.

복잡한 연관이 있는 모델에서 개체에 대한 변경 사항의 일관성을 보장하는 것은 어렵습니다. 개별 개체뿐만 아니라 밀접하게 관련된 개체 그룹에 적용되는 불변성을 유지해야 합니다. 그러나 신중한 잠금 방식은 여러 사용자가 서로 무의미하게 간섭하여 시스템을 사용할 수 없게 만듭니다.

1 먼저 모델 내에서 참조를 캡슐화하기 위한 추상화가 필요합니다. AGGREGATE는 데이터 변경을 위해 하나의 단위로 취급하는 관련 개체의 클러스터입니다. 각 AGGREGATE에는 루트와 경계가 있습니다. 경계는 AGGREGATE 내부에 있는 내용을 정의합니다. 루트는 AGGREGATE에 포함된 단일한 특정 ENTITY입니다. 루트는 경계 내의 객체가 서로에 대한 참조를 보유할 수 있지만 외부 객체가 참조를 보유할 수 있는 AGGREGATE의 유일한 구성원입니다.

이제 개념적 AGGREGATE를 구현으로 변환하려면 모든 트랜잭션에 적용할 일련의 규칙이 필요합니다. 
• 루트 ENTITY는 전역 ID를 가지며 궁극적으로 불변량 검사를 담당합니다. 
• 루트 ENTITIES는 글로벌 ID를 갖습니다. 경계 내부의 ENTITIES는 AGGREGATE 내에서만 고유한 로컬 ID를 갖습니다. 
• AGGREGATE 경계 외부의 어떤 것도 루트 ENTITY를 제외하고 내부에 대한 참조를 보유할 수 없습니다. 루트 ENTITY는 내부 ENTITIES에 대한 참조를 다른 개체에 전달할 수 있지만 해당 개체는 일시적으로만 사용할 수 있으며 참조를 보유하지 않을 수 있습니다. 루트는 VALUE OBJECT의 복사본을 다른 개체에 건네줄 수 있으며 어떤 일이 발생하든 상관없습니다. 왜냐하면 그것은 단지 VALUE이고 더 이상 AGGREGATE와 연관되지 않을 것이기 때문입니다. 
• 이전 규칙의 결과로 AGGREGATE 루트만 데이터베이스 쿼리를 통해 직접 얻을 수 있습니다. 다른 모든 개체는 연관 탐색을 통해 찾아야 합니다. 
• AGGREGATE 내의 개체는 다른 AGGREGATE 루트에 대한 참조를 보유할 수 있습니다. 
• 삭제 작업은 AGGREGATE 경계 내의 모든 항목을 한 번에 제거해야 합니다. (가비지 수집을 사용하면 쉽습니다. 루트 외에는 외부 참조가 없기 때문에 루트를 삭제하면 나머지는 모두 수집됩니다.) 
• AGGREGATE 경계 내의 개체에 대한 변경이 커밋되면 모든 불변 전체 AGGREGATE가 충족되어야 합니다.

ENTITIES 및 VALUE OBJECTS를 AGGREGATES로 클러스터링하고 각각 주변에 경계를 정의합니다. 각 AGGREGATE의 루트가 될 하나의 ENTITY를 선택하고 루트를 통해 경계 내부의 객체에 대한 모든 액세스를 제어합니다. 외부 개체가 루트에 대한 참조만 보유하도록 허용합니다. 내부 멤버에 대한 임시 참조는 단일 작업 내에서만 사용하기 위해 허용할 수 있습니다. 루트가 액세스를 제어하기 때문에 내부 변경으로 인해 외부에서 참조불가가 발생할수 없습니다. 

AGGREGATE로 라이프사이클을 캡슐화 하는 사례


객체 또는 전체 AGGREGATE 생성이 복잡해지거나 내부 구조가 너무 많이 드러날 때 FACTORIES는 캡슐화를 제공합니다. 객체의 힘의 대부분은 내부 및 연관의 복잡한 구성에 있습니다. 대상의 의미와 관련이 없거나 상호 작용에서 역할을 지원하지 않는 것이 남아 있지 않을 때까지 대상을 걸러야 합니다.

객체 생성은 그 자체로 주요 작업이 될 수 있지만 복잡한 조합 작업은 생성된 객체의 책임에 맞지 않습니다. 이러한 책임을 결합하면 이해하기 어려운 불쾌한 디자인이 생성될 수 있습니다. 클라이언트를 직접 구성하게 하면 클라이언트의 디자인이 흐려지고 어셈블된 개체 또는 AGGREGATE의 캡슐화를 위반하고 클라이언트를 생성된 개체의 구현에 과도하게 연결합니다.

복잡한 객체 생성은 도메인 계층의 책임이지만 그 작업은 모델을 표현하는 객체에 속하지 않습니다. 객체 생성 및 조립이 "은행 계좌 개설"과 같이 도메인에서 중요한 이정표에 해당하는 경우가 있습니다. 다른 개체의 생성을 책임지는 프로그램 요소를 FACTORY라고 합니다.




복잡한 개체와 집합체의 인스턴스 생성에 대한 책임을 별도의 개체로 옮기십시오. 이 개체는 도메인 모델에서 책임이 없지만 여전히 도메인 디자인의 일부입니다. 모든 복잡한 조합을 캡슐화하고 클라이언트가 인스턴스화되는 개체에 대한 구체적인 클래스를 참조할 필요가 없도록 인터페이스를 제공합니다.

좋은 FACTORY가 되기 위한 기본적인 요구사항은 다음과 같습니다.
1. 각 생성 방법은 원자적이며 생성된 개체 또는 AGGREGATE의 모든 불변성을 적용합니다. FACTORY는 일관된 상태에서만 개체를 ​​생성할 수 있어야 합니다. ENTITY의 경우 이는 전체 AGGREGATE의 생성을 의미하며 모든 불변조건을 충족되지만 선택적 요소가 여전히 추가될 수 있습니다. 변경할 수 없는 VALUE OBJECT의 경우 이는 모든 속성이 올바른 최종 상태로 초기화됨을 의미합니다. 올바르게 생성할 수 없는 객체를 인터페이스가 가능하게 해야할 경우, 예외를 발생시키거나 부적절한 반환이 안되도록 다른 메커니즘이 호출되어야 합니다. 

2. FACTORY는 생성된 구체적인 클래스가 아니라 원하는 유형으로 추상화되어야 합니다. 

일반적으로 말해서 세부 사항을 숨기고 싶은 무언가를 만들기 위해 FACTORY를 만들고 제어할 위치에 FACTORY를 배치합니다. 이러한 결정은 일반적으로 AGREGATES를 중심으로 이루어집니다. 예를 들어, 기존 AGGREGATE 내부에 요소를 추가해야 하는 경우 AGGREGATE의 루트에 FACTORY METHOD를 만들 수 있습니다. 이것은 외부 클라이언트로부터 AGGREGATE 내부의 구현을 숨기는 동시에 다음 페이지의 그림과 같이 요소가 추가될 때 AGGREGATE의 무결성을 보장하는 루트 책임을 부여합니다.

FACTORY는 이것의 Product와 매우 밀접하게 결합되어 있으므로 FACTORY는 Product와 자연적으로 밀접한 관계가 있는 Object에만 연결되어어야 합니다. 구체적인 구현이나 구성의 순전한 복잡성 중 숨기고 싶은 것이 있지만 자연스러운 호스트가 없는 것 같으면 전용 FACTORY 개체 또는 SERVICE를 만들어야 합니다. 독립형 FACTORY는 일반적으로 전체 AGGREGATE를 생성하여 루트에 대한 참조를 전달하고 product AGGREGATE의 불변을 강제합니다. AGGREGATE에 대한 객체 내부에 FACTORY가 필요하고 AGGREGATE 루트가 이에 대한 합리적인 홈이 아닌 경우 독립 실행형 FACTORY를 만드십시오. 하지만 AGGREGATE 내 접근을 제한하는 규정을 준수하고 AGGREGATE 외부에서 일시적인 참조만 있는지 확인해라. 

FACTORIES는 실제로 다형성을 사용하지 않는 단순한 객체를 모호하게 할 수 있습니다. 절충안은 다음과 같은 상황에서 노출된, Public constructor를 선호합니다. 
• 클래스는 Type입니다. 계층 구조의 일부가 아니며 다형적으로 사용되지 않습니다. 인터페이스를 구현합니다.
• 클라이언트는 아마도 전략을 선택하는 방법으로써 구현에 관심을 갖습니다
• 개체의 모든 속성을 클라이언트에서 사용할 수 있으므로 개체 생성이 클라이언트에 노출된 constructor 내부에 중첩되지 않습니다.
• 구성이 복잡하지 않습니다.
• Public constructor는 FACTORY와 동일한 규칙을 따라야 합니다. 생성된 object의 불변하도록 원자적 연산이어야 합니다.

다른 클래스의 constructor 내에서 constructor를 호출하지 마십시오. 생성자는 간단해야 합니다. 특히 AGREGATES의 복잡한 어셈블리에는 FACTORIES가 필요합니다. 


ENTITY FACTORIES Versus VALUE OBJECT FACTORIES 
ENTITY FACTORIES는 VALUE OBJECT FACTORIES와 두 가지 면에서 다릅니다. VALUE OBJECTS는 변경할 수 없습니다. Product는 최종 형태로 완성됩니다. 따라서 FACTORY 작업은 제품에 대한 전체 설명을 허용해야 합니다.
반면에 ENTITY FACTORIES는 유효한 AGGREGATE를 만드는 데 필요한 필수 속성만 취하는 경향이 있습니다. 세부 사항을 나중에 추가할 수 있습니다.


Repositories
클라이언트는 기존 도메인 개체에 대한 참조를 획득하는 실질적인 수단이 필요합니다. 인프라에서 쉽게 할 수 있는 경우 클라이언트 개발자는 더 많은 순회적인 연결을 추가하여 모델을 혼란스럽게 할 수 있습니다. 반면에 쿼리를 사용하여 데이터베이스에서 필요한 정확한 데이터를 가져오거나 AGGREGATE 루트에서 탐색하는 대신 몇 가지 특정 개체를 가져올 것입니다. 도메인 로직은 쿼리 및 클라이언트 코드로 이동하고 ENTITIES 및 VALUE OBJECTS는 단순한 데이터 컨테이너가 됩니다. 대부분의 데이터베이스 액세스 인프라를 적용하는 데 따르는 기술적인 복잡성은 클라이언트 코드를 빠르게 휩쓸고, 이는 개발자가 도메인 계층을 벙어리로 만들어 모델을 부적절하게 만듭니다.

영구 개체의 하위 집합은 개체 속성을 기반으로 하는 검색을 통해 전역적으로 액세스할 수 있어야 합니다. 이러한 액세스는 순회로 도달하기가 쉽지 않은 AGREGATES의 루트에 필요합니다. 그것들은 일반적으로 ENTITIES, 때로는 복잡한 내부 구조를 가진 VALUE OBJECTS, 때로는 열거된 VALUES입니다. 다른 개체에 대한 액세스를 제공하면 중요한 구분을 모호하게 합니다. 자유로운 데이터베이스 쿼리는 실제로 도메인 개체 및 집합체의 캡슐화를 위반할 수 있습니다. 기술 인프라 및 데이터베이스 액세스 메커니즘의 노출은 클라이언트를 복잡하게 만들고 MODEL-DRIVEN DESIGN을 모호하게 만듭니다.

전역 액세스가 필요한 각 개체 유형에 대해 해당 유형의 모든 개체에 대한 인메모리 컬렉션 환상(illusion)을 제공할 수 있는 개체를 만듭니다. 잘 알려진 글로벌 인터페이스를 통해 액세스를 설정합니다. 데이터 저장소에서 데이터의 실제 삽입 또는 제거를 캡슐화하는 개체를 추가 및 제거하는 방법을 제공합니다. 일부 기준에 따라 개체를 선택하고 속성 값이 기준을 충족하는 완전히 인스턴스화된 개체 또는 개체 컬렉션을 반환하는 메서드를 제공하여 실제 스토리지 및 쿼리 기술을 캡슐화합니다. 실제로 직접 액세스해야 하는 AGGREGATE 루트에 대해서만 REPOSITORIES를 제공하십시오. 클라이언트가 모델에 집중하도록 하십시오. 모든 객체 스토리지를 위임하고, REPOSITORIES에 엑세스합니다.