06 - 도메인 객체의 생명주기

  • 모든 객체에는 생명주기가 있다. 한 객체는 생성되어 다양한 상태를 거친 후 결국 저장되거나 삭제되면서 소멸한다.

  • 물론 이들 중 상당수는 객체에 생성자를 호출해서 만들어진 다음 특정 연산에서 사용된 후 가비지 컬렉터에게 보내지는 단순하고 일시적인 객체이다. 이런 객체는 복잡하게 만들 필요가 없다.

  • 그러나 일시적이지 않은 객체들은 더 오래 지속되며, 다른 객체와 복잡한 상호작용을 맺는다.

  • 이러한 객체들을 잘 관리하는 것이 DDD 에서 중요한 점이다. (도메인을 잘 이해해야 각각의 객체를 잘 구분할 수 있다)

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

  1. 생명주기 동안의 무결성 유지하기

  2. 생명주기 관리의 복잡성으로 모델이 난해해지는 것을 방지하기

  • 이를 위한 해결책으로 AGGREGATE, FACTORY, REPOSITORY 가 있다.

AGGREGATE (집합체)

  • 객체들이 밀접한 연관관계를 맺는 객체 집합에는 불변식이 적용되어야한다. (일관성 있는 객체 상태를 유지하기 위해서)

불변식 : 데이터가 변경될 때마다 유지되어야 하는 규칙

  • 모델 내에 참조에 대한 캡슐호하의 추상화가 필요하고, 그것이 바로 Aggreate(데이터 변경의 단위로 다루는 연관 객체의 묶음)이다.

    • 루트(Root) 와 경계(Boundary) 가 있다.

Aggregate 구현 규칙

  • 루트 Entity는 전역 식별성을 지니며, 궁극적으로 불변식을 검사할 책임이 있다.

  • 각 루트 Rntity 는 전역 식별성을 지닌다. 경계 안의 Entity 는 지역 식별성을 지니며, 이러한 지역 식별성은 해당 Aggregate 안에서만 유일하다.

  • Aggreate 경계 밖에서는 루트 Entity 를 제외한 Aggreate 내부의 구성요소를 참조할 수 없다.

  • 데이터베이스 Query 를 이요하면 Aggreate 의 루트만 직접적으로 획득할 수 있다.

  • Aggregate 안의 객체는 자신이 속한 루트만 참조할 수 있다.

  • 삭제 연산은 Aggregate 경계 안의 모든 요소를 한 번에 제거해야 한다.

  • Aggregate 경계 안의 어떤 객체를 변경하더라도 전체 Aggregate 의 불변식은 모두 지켜져야 한다.

Aggregate 정리

  • Entity 와 Value Object 를 Aggregate 로 모으고 각각에 대해 경계를 정의하라.

  • 한 Entity 를 골라 Aggregate Root 로 만들고, Aggregate 경계 내부에 객체에 대해서는 루트를 거쳐 접근할 수 있도록 하라.

  • Aggregate 밖의 객체는 루트만 참조할 수 있도록 하라.

  • 내부 구성요소에 대한 일시적인 참조는 단일 연산에서만 사용할 목적에 한해 외부로 전달될 수 있다.

  • 루트를 경유하지 않고 Aggregate 의 내부를 변경할 수 없다.

  • 이런식으로 Aggregate 의 각 요소를 배치하면 Aggregate 안의 객체와 전체로서의 Aggregate 의 상태를 변경할 때 모든 불변식을 효괒정으로 이행할 수 있다. (데이터 무결성을 지키는 좋은 방법)

FACTORY

팩토리 메서드 패턴 https://refactoring.guru/ko/design-patterns/factory-method

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

  • 어떤 객체를 생성하는 것이 그 자체로도 주요한 연산이 될 수 있지만, 복잡한 조립 연산은 생성된 객체의 책임으로 어울리지 않는다. 이런 책임을 클라이언트에 두면 이해하기 힘든 볼품없는 설계로 이어질 수 있다. (또한 조립 연산은 클라이언트의 책임이라 보기 어렵다..)

    • 우리는 자동차를 운전할 때 어떻게 조립 되었느냐는 신경쓰지 않는다. 두 가지 행위는 같은 시간에 일어나지 않으며, 이런 기능이 동일한 매커니즘에 결합되어 있는 것은 의미가 없기 때문이다.

  • 복잡한 객체와 Aggregate 인스턴스를 생성하는 책임을 별도의 객체로 옮겨라. (Factory)

  • 복합한 객체를 생성한다는 것은 도메인의 책임이기는 하나 그것이 업무의 모델을 표현하는 것은 아니다.

    • 일반적으로 객체의 생성과 조립은 도메인에서 의미가 없다. 하지만 구현 측면에서 반드시 필요하다.

Factory 와 위치 선정

  • Factory 를 만들기로 했다면 제어할 수 있는 어딘가에 위치시켜야 한다. 위치를 결정할때는 보통 Aggregate 를 중심으로 판단한다.

  • Factory 생성이 가능한 위치는 다음과 같다 .

    • Aggregate Root : 정적 메서드를 통한 Factory

    • Domain Service : Service 기반 Factory

    • ...

  • 이미 존재하는 Aggregate 에 특정 요소를 추가하는 경우를 생각해보자.

  • 위 다이어그램은 기존에 존재하는 Aggregate PO 에 Line item 요소를 추가하는 과정을 나타낸것이다. Client 는 새로운 item 에 대한 PartID 와 수량만 넘겨주면 Aggregate 는 Line Item 을 추가하고 무결성을 보장하기 위해서 불변식 실행을 담당한다.

  • Factory Method 가 반드시 같은 Aggregate 내의 객체 생성만 담당하는 것은 아니다. 객체간의 밀접한 관련이 있는 경우 다른 Aggregate 의 객체 생성을 담당하기도 한다.

Factory vs 생성자

  • 객체를 생성할 때 Factory 를 사용하면 많은 이점이 있지만, 반드시 그래야하는 것은 아니다. 때로는 간단한 생성자를 사용하는 것이 더 좋을때도 있다. 만약 아래와 같은 상황이라면 공개 생성자를 사용하는 방식을 고려해보자.

    • 클래스가 곧 타입인 경우 : 어떤 계층구조도 가지고 있지 않으며, 인터페이스를 구현하는 다형성으로 사용되지 않는 경우

    • 클라이언트가 전략을 선택하는 방법으로 구현체에 관심 있는 경우

    • 생성자가 복잡하지 않은 경우

불변식 로직의 위치

  • Factory 는 자신의 생성물과 특별한 관계를 맺는다. 해당 생성물에 대해 내부 구현을 알고 있으며 밀접한 관계를 갖기 떄문이다.

  • 이런 경우 불변식이 생성자가 아닌 Factory 에 위치 시킨다면 생성물의 복잡도를 줄이는데 도움이 된다.

  • 단 주의할 점은 위와 같은 경우 Aggregate 규칙에는 잘 맞지만 다른 도메인 객체에 속한 객체를 생성하는 Factory Method 에 경우에는 적용되지 않는다.

REPOSITORY

  • 기존에는 DB 와 객체간의 패러다임 불일치 해결을 위한 기술적인 이슈 떄문에 많은 에너지를 소모하게 되었고, 그로 인해 도메인 자체의 집중력이 떨어지고 도메인에 관한 내용도 객체 DB 매핑 전에 들어가기 시작했다.

  • 그러한 이슈를 해결하기 위해서 인프라스트럭처에 접근하는 행위도 캡슐화하고 DB 에 의존적이지 않고 순수한 객체로 핸들링할 수 있는 수단이 필요했다. 그것이 Repository 가 해결해준다.

    • 객체 재구성의 책임 (DB, OOP 간의 패러다임 불일치 해결)

    • 영속화 객체 접근의 캡슐화

  • 클라이언트는 Repository 를 사용하므로써 각 객체 타입에 대해 메모리상에 해당 타입의 객체로 구성된 컬랙션이 있다고 착각을 불러 일으키는 객체를 만든다.

  • Aggregate 루트에 대해서만 Repository 를 제공하고, 모든 객체 저장과 접근은 Repository 에 위임해서 클라이언트가 모델에 집중하게 한다.

Repository 이점

  • Repository 는 영속화된 객체를 획득하고, 해당 객체의 생명주기를 관리하기 위해 단순한 모델을 클라이언트에 제시한다.

  • Repository 는 영속화 기술과 다수의 데이터베이스 전략, 또는 심지어 다수의 데이터 소스로부터 애플리에이션과 도메인 설계를 분리한다.

  • Repository 는 객체 접근에 대한 메서드를 제공해준다.

  • Repository 를 이용하면 테스트에서 사용할 Mock 객체를 손쉽게 만들 수 있다.

프레임워크 활용

  • 직접 구현은 쉽지 않다.

  • 최근에는 Repository 를 구현하는 ORM 기술과 같은 것들을 사용할 수 있다 .

Last updated