- 모든 객체에는 생명주기가 있다
한 객체는 생성되어 다양한 상태를 거친 후 결국 저장되거나 삭제되면서 소멸한다 - p127 - 도메인 객체의 관리와 관련된 문제(와 해결 패턴 3가지) - p128
1) 생명주기 동안의 무결성 유지
2) 생명주기 관리의 복잡성으로 모델이 난해해지는 것 방지
위의 문제를 아래 3가지 패턴을 이용해 해결
06-1) Aggregate 집합체 : 소유권과 경계를 명확히 정의, 객체간의 연관관계가 혼란스럽지 않도록
생명주기 전 단계에 걸쳐 모데인 객체의 무결성을 유지하는데 매우 중요
06-2) Factory : 생명주기 초기단계, 복잡한 객체와 Aggreage를 생성하고 재구성, 내부 구조를 캡슐화
06-3) Repository : 생명주기 중간과 마지막, Infrastructure를 캡슐화, 영속 객체를 찾아 조회
06-1) Aggregate 집합체
- 대부분의 업무 도메인은 상호 연관의 정도가 높으므로 결국 우리는 객체 참조를 통해 얽히고 설킨 관계망을 추적해야 한다 - p129
- 한 사람은 이름, 생년월일, 직업 등의 설명이 필요
하지만 주소는? 같은 주소를 쓰는 사람이 있을 수 도 있다
주소를 삭제한다면 다른 Person챛게는 삭제된 객체를 참조하게 될 것이다
그렇다고 그러한 참조를 그대로 둔다면 DB에는 쓰레기 주소가 쌓인다
(Garbage Collection 또는 DB에서 알아서 제거해주거나 나름대로 관리할수도 있지만)
기술적인(근본적인) 해법은 기본적인 모델링 문제를 간과(그렇지 않도록 모델링되어야함) 하는 것에 해당 - p129 - (여러 객체가 단 덩어리로의 범위)가 어디서 부터 어디까지인지 어떻게 알 수 있을까? > 모델의 경계 정의 필요
모델을 근간으로 하는 해법을 이용하면 모델을 좀 더 이해하기 쉬움 - p130 - Aggregate 정의 : 데이터 변경의 단위로 다루는 연관 객체의 묶음 - p131
- Aggregate 특징 - p131
- Aggregate에는 Root와 경계(Boundary)가 있다
- Boundary는 Aggregate에 무엇이 포함되고 포함되지 않는지 정의
- Root는 단 하나만 존재하고 Aggregate에 포함된 특정 Entity를 Root로 삼는다
- Boundary안의 객체는 서로 참조할 수 있지만, Boundary바깥의 객체는 해당 Aggregate의 Root만 참조 가능
- Root 이외의 Entity는 지역 식별성(Local Identity)을 지니고, Local Identity는 Aggregate내에서만 구분되면 됨
- Aggregate Boundary 밖에 위치한 객체는 Root Entity말고는 Aggregate 내부를 볼 수 없음
- 예 : 자동자는 전역 식별성(차량식별번호)을 지닌 Entity, 타이어도 Entity, 자동차는 타이어도 포함하는 경계를 지닌
Aggregate의 Root Entity, 그림 6-2 지역 및 전역 식별성과 객체 참조 - p132 - Aggregate 의 불변식, 그림 6-3. p132 (<<entity>>, <<root>> 별도의 stereotype 사용)
불변식은 데이터가 변경될 때 마다 유지돼야 하는 일관성 규칙
여러 Aggregate에 걸쳐 존재하는 규칙이 언제나 최신 상태로 유지되는 것은 아니다
다른 의존 관계는 이벤트 처리, 배치 처리, 혹은 다른 갱신 매커니즘을 토대로 특정 시간 내에 해결될 수 있다
반면에 한 Aggregate에 적용된 불변식은 각 Transaction이 완료될 때 이행
(BASE의 개념과 유사?) - 개념적 Aggregate를 구현하기 위한 규칙 필요 - p133
- Root Entity는 전역 식별성을 지니며 불변식을 검사할 책임이 있다
- DB Query를 이용하면 Aggregate의 Root만 직접 획득하고 다른 객체는 모두 Aggregate를 탐색?해서 발견해야 함
- 삭제 연산은 Aggregate 경계안의 모든 요소를 한번에 제거해야 함
- Aggregate 경계 안의 어떤 객체를 변경하더라도 전체 Aggregate의 불변식은 모두 지켜져야 함 - 주문관련 예 참고 - p134
Aggregate는 업무 규칙에 맞는 주문(Order)과 주문품목(Order Line)에 대한 소유권한 부여
주문(Order)과 주문품목(Order Line)의 생성과 삭제는 함께 묶이는 반면,
품목(Part Master)의 생성과 삭제는 독립적으로 이뤄진다 (Aggregate가 다르므로) - Aggregate는 생명주기의 전 단계에서 불변식이 유지돼야 할 범위를 표시
다음에 이어지는 Factory와 Repository는 Aggregate를 대상으로 연산을 수행하며, 특정 생명 주기 전이에 따르는 복잡성을 캡슐화 한다 (Aggregate 내 객체 묶음 알아서 불변성, 규칙 등에 맞게 생성, 수정, 삭제해주는 패턴) - p139
06-2) Factory
- 어떤 객체나 전체 Aggregate를 생성하는 복잡함 등을 캡슐화하는 패턴 - p140
- (Domain Layer가 아닌 Application 등 Client 에서 Aggregate를 생성/조작할 때 문제점) - p141
- Client에서 도메인 객체를 직접 조립해야 한다면 Client는 도메인 객체의 내부구조를 어느정도 알아야함
- Aggregate내 불변식을 이행하려면 Client에서 해당 객체의 규칙을 어느정도 알아야 함
- 이렇게 되면 생성자를 호출하는 것만으로도 생성 중인 객체의 클래스와 Client가 결함됨
- 객체 생성을 맡은 Client는 불필요하게 복잡해지고 Client가 맡고 있는 책임은 불분명해짐
- 그 Client는 도메인 객체와 생성된 Aggregate의 캡슐화 위반
- 더욱 안좋은 점은 Client가 Application Layer의 일부를 구성하고 있다면
Domain Layer에서 책임이 새어 나온다는 것
- 이처럼 Application이 구현의 세부사항에 강하게 결합되면 Domain Layer을 추상화해서 얻을 수 있는 이점이 없고
훨씬 더 많은 비용이 들고, 변경사항이 계속해서 나타나게 됨(추가/변경 요구사항 대응이나 유지보수도 어려워짐)
- 결국 Client에서 필요로 하는 객체를 직접 생성하면 Client설계가 지저분해지고,
조립되는 객체나 Aggregate의 캡슐화를 위반하고,
Client와 생성된 객체의 구현이 지나치게 결합
- 객체 생성이나 조립관련 문제를 해결하려면 Entity나 VO, Service가 아닌 (별도의) 구조물을 도메인에 추가 필요 - 다른 객체를 생성하는 책임을 갖는 요소를 Factory - p142, 그림 6-12
Factory는 복잡한 객체나 Aggregate를 생성하는데 필요한 지식을 캡슐화
Client의 목적과 생성된 객체의 추상적인 관점을 반영하는 인터페이스를 제공 - Factory 설계 방법 - p143
- Factory Method : Aggregate Root에 Factory Method를 만들수도 있음
- Abstract Factory : Standalone 독립형 Factory, 전용 Factory객체나 Service 도 가능, 그림 6-15
- Builder - 저장된 객체의 재구성, 초기 생성용 Factory와 유사하며 다음의 차이점이 있음 - p149
- 재구성에 사용된 Entity Factory는 새로운 ID를 할당하지 않는다
Factory 의 입력 매개변수에 반드시 식별 속성을 포함
- 객체를 재구성하는 Factory는 불변식 위반을 다른 방식으로 처리할 것이다
06-3) Repository
- 객체간의 연관관계를 토대로 특정 객체를 찾을 수 있어야한다
객체의 생명주기 중간에도 Entity나 Value를 탐색하기(조회/변경) 위한 진입점 필요 - p152 - 재구성(Reconstitution) : 저장돼 있는 객체로부터 인스턴스를 만들어 내는 것
(DB를 조회하여 Aggregate내 규칙을 준수하여 객체들을 만들어 냄) - p154 - DDD의 목표 : 기술보다는 도메인에 대한 모델에 집중해 더 나은 SW를 만들어내는 것 - p154
- (우리의 현실) - p154
Client는 이미 존재하는 도메인 객체의 참조를 획득하는(DB조회/저장을 필요로함)
Aggregate Root를 통하지 않고 필요한 데이터를 DB에서 바로 조회
Domain Logic 이라기 보다는 Query와 Infra(Data Access) 코드로 구성되고
Entity와 VO는 그저 데이터 Container(Data Transfer)로 전락 ... - Aggregate 내부에 존재하는 모든 객체는 Root에서부터 탐색을 토대로 접근하는 것 말고는 접근이 금지돼야함 - p155
- Repository의 이점 - p157
- 영속화된(데이터 저장소에 저장된) 객체를 획득
- 영속 기술, DB 전략, 다수의 Data Source로 부터 Application과 Domain 설계를 분리
- Test에서 Fake(Mocking) Repository 대체 가능(메모리상의 Collection, Hard Coding 등) - Repository에 Query - p158
- 식별자를 기준으로한 Entity 조회 (예 : findById)
- 특정 속성값이나 복잡한 매개변수 연산을 토대로한 객체 컬렉션 요청 등(예 : findByNameOrType ... ) - 영속화 기술을 캡슐화하면 Client가 매우 단순해지고 Repository 구현에서 완전히 분리 - p159
- Repository 구현 - p160
- 데이터가 OODB든 RDB에 저장되든 아니면 단순히 메모리상에 상주하든 관계없이 Client 코드가 동일하게 유지
- Repository는 적절한 Infrastructure 서비스에 작업을 위임해서 본인의 작업을 완수
- 저장, 조회 매커니즘을 캡슐화하는것은 Repository 구현의 가장 기본적인 기능 - Repository 관련 주의사항 - p161
- Repository각 특정 Type의 모든 인스턴스를 담기는 하지만
이것이 각 클래스마다 하나의 Repository가 필요하는 의미는 아니다
(예 : Aggreage : Repository?, Entity : Repository? 등의 관계 고민 필요)
- Trsnsaction 제어를 Client에 둔다
Repository에서는 마우것도 Commit하지 않을 것이다
Client (or Framework 이용) 에서 단위 작업 후 Commit 하는 Context 필요 - Factory와의 관계 - p163
- Factory가 새로운 객체를 만들어 내는데 반해 Repository는 기존 객체를 찾아낸다
- 데이터를 근거로 객체를 생성하므로 기술적인 관점에는 Factory와 유사
- Factory에서 영속화에 대한 모든 책임을 분리, 그림 6-22, p164 - RDB와 객체설계 - p167
- 객체지향 도메인을 영속적인 형태로 표현하는 RDB의 경우, 단순하게 직접적으로 표현하는 편이 좋다
- 하나의 Table Record는 하나의 객체를 담고 있어야
- Table의 FK는 또 다른 Entity 객체에 대한 참조로 변환해야 함
- UL는 객체와 RDB를 하나의 모델로 묶는데 이바지
객체를 구성하는 각 요소의 이름과 연관관계는 RBD Table을 구성하는 요소의 이름과 대응해야함
'DDD' 카테고리의 다른 글
08장_도약 (0) | 2021.10.07 |
---|---|
07장_언어의 사용(확장 예제) (0) | 2021.10.07 |
10장_유연한 설계 (0) | 2021.10.07 |
05장_소프트웨어에서 표현되는 모델(Entity, VO, Service, Module) (0) | 2021.10.05 |
04장_도메인의 격리(Layered Architecture중 Domain) (0) | 2021.10.05 |