[DDD Start] 6. 도메인 객체의 생명주기

도메인 객체의 생명주기

도메인 객체의 관리와 관련된 문제는 두 가지 범주로 나뉜다.

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

DDD는 이런 문제를 해결 하기 위해 3가지 패턴

AGGREGATE를 모델링하고 설계에 FACTORY와 REPOSITORY를 추가하면 모델 객체의 생명주기 동안 그것들을 체계적이고 의미 있는 단위로 조작.

AGGREGATE 는 생명주기의 전 단계에서 불변식이 유지돼야 할 범위를 표시해 준다. 그리고 FACTORYREPOSITORYAGGREGATE 를 대상으로 연산을 수행하며 특정 생명주기로 옮겨가는데 따르는 복잡성을 캡슐화.

AGGREGATE(집합체)

우리가 데이터 변경의 단위(TransAction 이 될 수 있음.)로 다루는 연관 객체의 묶음을 말함

모델 내에서 복잡한 연관관계를 맺는 객체를 대상으로 변경의 일관성을 보장하기란 쉽지 않다. 그 까닭은 단지 개별 객체만이 아닌 서로 밀접한 관계에 있는 객체 집합에도 불변식이 적용돼야 하기 때문이다. 그렇다고 변경의 일관성을 보장하고자 신중 잠금 기법을 쓴다면 다수의 사용자가 서로 부적절하게 간섭해서 시스템이 사용할 수 없는 상태가 될 것.

AGGREGATE는 루트(root)와 경계(boundary) 가 존재.

개념적인 AGGREGATE를 구현하기 위해서는 다음과 같은 규칙이 필요하다.

AGGREGATE 는 생명주기의 전 단계에서 불변식이 유지돼야 할 범위를 표시ㅣ.

FACTORY(팩토리)

어떤 객체나 전제 AGGREGATE 를 생성하는 일이 복잡해지거나 내부 구조를 너무 많이 드러내는 경우 FACTORY가 캡슐화 제공.

어떤 객체를 생성하는 것이 그 자체로도 주요한 연산이 될 수 있지만 복잡한 조립 연산은 생성된 객체의 책임으로 어울리지 않는다. 이런 책임을 클라이언트에 두면 이해하기 힘든 볼품없는 설계가 만들어질 수 있다. 클라이언트에서 직접 필요로 하는 객체를 생성하면 클라이언트 설계가 지저분해지고 조립되는 객체나 AGGREGATE의 캡슐화를 위반하며, 클라이언트와 생성된 객체의 구현이 지나치게 결합된다.

자신의 책임이 다른 객체를 생성하는 것인 프로그램 요소를 FACTORY라 한다.

image-20200403160912901

복잡한 객체와 AGGREGATE의 인스턴스를 생성하는 책임을 별도의 객체로 옮겨라. 이 객체 자체는 도메인 모델에서 아무런 책임도 맡지 않을 수도 있지만 여전히 도메인 설계의 일부를 구성한다. 모든 복잡한 객체 조립 과정을 캡슐화하는 동시에 클라이언트가 인스턴스되는 객체의 구상 클래스를 참조할 필요가 없는 인터페이스를 제공하라.

전체 AGGREGATE를 하나의 단위로 생성해서 그것의 불변식이 이행되게 하라.

FACTORY를 잘 설계하기 위한 두 가지 기본 요건

  1. 각 생성 방법은 원자적(atomic)이어야 하며, 생성된 객체나 AGGREGAT의 불변식을 모두 지켜야 한다. FACTORY는 일관성 있는 상태에서만 객체를 만들어 낼 수 있어야 한다. ENTITY의 경우 이것은 전체 AGGREGATE를 생성하는 것을 의미하며, 이때 모든 불변식을 충족하고 선택적인 요소도 추가 될 것. 불변적인 VALUE OBJECT의 경우에는 모든 속성이 올바른 최종 상태로 초기화된다는 것을 의미. 인터페이스를 통해 올바르게 생성할 수 없는 객체를 요청할 수 있다면 예외가 발생하거나 또는 다른 어떤 메커니즘이 작동해서 더는 적절하지 않은 반환값이 사용될 수 없도록 보장.
  2. FACTORY는 생성된 클래스보다는 생성하고자 하는 타입으로 추상화돼야 한다. 우리가 알고 있는 팩토리 패턴이 대표적인 예가 된다.

FACTORY METHOD는 동일한 AGGREGATE에 속하지 않는 ENTITY를 만들어 낸다.

FACTORY 의 인터페이스 설계

해당 FACTORY가 독립형이냐? FACTORY METHOD냐? 에 관계없이 두 가지 사항을 명심.

요약하면, 인스턴스 생성을 위한 접근 시점을 식별해야 하며, 그러한 접근 지점의 범위는 명시적으로 정의. 그와 같은 접근 지점은 단순히 생성자일 수도 있지만 간혹 더 추상적이거나 정교한 인스턴스 생성 메커니즘이 필요할 때도 있다. 이러한 필요에 따라 새로운 구조물인 FACTORY가 설계에 도입. 일반적으로 FACTORY는 모델의 어떤 부분도 표현하지는 않지만 해당 모델을 나타내는 객체를 뚜렷하게 드러내는 데 일조하는 도메인 설계의 일부로 볼 수 있다.

FACTORY는 객체의 생성과 재구성이라는 생명주기 전이(transition)를 캡슐화한다. 도메인 설계를 난해하게 할 수 있는 기술적 복잡성을 노출하는 또 하나의 생명주기 전이로는 저장소에 들어갈 때나 저장소에서 나올 때 거치는 전이가 있다. 이러한 전이는 또 다른 도메인 설계 구조물인 REPOSITORY의 책임

REPOSITORY

객체의 생명주기 중간에도 ENTITY나 VALUE를 탐색하기 위한 진입점이 있어야 한다.

클라이언트는 이미 존재하는 도메인 객체의 참조를 획득하는 실용적인 수단을 필요로 한다. 인프라스트럭처에서 도메인 객체의 참조를 쉽게 획득할 수 있게 해준다면 클라이언트 측을 개발하는 개발자들이 좀더 탐색 가능한 연관관계를 추가해 모델을 엉망으로 만들어 버릴지도 모른다. 한편으로는 AGGREGATE 루트에서부터 순회하지 않고 정확히 필요한 데이터를 데이터베이스에서 뽑아내거나 몇 가지 특정한 객체를 가져오는 데 질의를 사용할 수도 있다. 도메인 로직은 질의와 클라이언트 코드로 들어가고, ENTITY와 VALUE OBJECT는 그저 데이터 컨테이너로 전락한다. 대부분의 데이터 접근 인프라스트럭처를 적용하는 데 따르는 급격한 기술적 복잡성으로 클라이언트 코드는 금방 복잡해지고, 이는 도메인 계층에 대한 개발자들의 이해 수준을 낮춰 모델의 도메인 계층과 동떨어진 것을 만든다.

AGGREGATE 내부에 존재하는 모든 객체는 루트에서부터 탐색을 토대로 접근하는 것 말고는 접근이 금지돼 있다는 점.

영속 객체는 해당 객체의 속성에 근거해서 검색하는 식으로 전역적으로 접근할 수 있어야 한다. 그러한 접근 방식이 필요한 곳은 탐색으로 도달하기에는 편리하지 않은 AGGREFATE의 루트다. 일반적으로 루트는 ENTITY이며, 간혹 복잡한 내부 구조를 지닌 VALUE OBJECT 이거나 열거형 VALUE 이기도 하다. 다른 객체에도 접근할 수 있게 한다면 중요한 구분법이 혼동 될 것이다.

마음대로 데이터베이스에 질의를 수행하면 실제로 도메인 객체와 AGGREGATE의 캡슐화를 어길 수도 있다. 기술적 인프라스트럭처와 데이터베이스 접근 메커니즘을 드러내면 클라이언트가 복잡해져서 MDD이 불분명.

우리는 더 이상 도메인 모델에 포함된 개념에 관해서는 생각하지 않고 있다. 이렇게 되면 우리가 작성하는 코드는 업무에 관해 전해주지 않을 것이며, 코드는 데이터 조회 기술을 다루게 될 것이다. REPOSITROY 패턴은 그와 같은 해법을 캡슐화해서 우리는 다시 모델에 집중하게 해주는 단순한 개념적 틀에 해당.

REPOSITORY는 특정 타입의 모든 객체를 (대개 모방된) 하나의 개념적 집합으로 나타낸다. 더욱 정교한 질의 기능이 있다는 점을 제외하면 REPOSITORY는 컬렉션처럼 동작. REPOSITORY에는 적절한 타입의 객체가 추가되고 제거되며, 이러한 REPOSITORY 이면에 존재하는 장치가 그러한 객체를 데이터베이스에 삽입하거나 삭제. 이 같은 정의에는 생명주기의 초기 단계에서 마지막 단계에 이르기까지 AGGREGATE의 루트에 대한 접근을 제공하는 각종 응집력있는 책임이 포함

REPOSITORY클라이언트가 진 큰 부담을 덜어줘서 이제 클라이언트는 단순하고 의도를 드러내는 인터페이스로 소통하고, 모델의 측면에서 필요로 하는 것들을 요청할 수있게 된다.

전역적인 접근이 필요한 각 객체 타입에 대해 메모리상에 해당 타입의 객체로 구성된 컬렉션이 있다는 착각을 불러 일으키는 객체를 만든다. 잘 알려진 전역 인터페이스를 토대로 한 접근 방법을 마련하라. 객체를 추가하고 제거하는 메서드를 제공하고, 이 메서드가 실제로 데이터 저장소에 데이터를 삽입하고 데이터 저장소에서 제거하는 연산을 캡슐화하게 하라 특정한 기준으로 객체를 선택하고 속성값이 특정 기준을 만족하는 완전히 인스턴스화된 객체나 객체 컬렉션을 반환하는 메서드를 제공함으로써 실제 저장소와 질의 기술을 캡슐화하라. 실질적으로 직접 접근해야 하는 AGGREGATE의 루트에 대해서만 REPOSITORY를 제공하고, 모든 객체 저장과 접근은 REPOSITORY에 위임해서 클라이언트가 모델에 집중하게 하라.

REPOSITORY의 이점

REPOSITORY의 구현할 때 명심해야 할 사항