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

본 포스트는 도메인 주도 설계 라는 책을 참조하였습니다. 가능하면 본 책을 직접 보시고 DDD를 이해하는 것을 추천드립니다. http://www.aladin.co.kr/shop/wproduct.aspx?ItemId=12174216

도메인객체_라이프사이클

그림 1) 도메인 객체 생명주기


도메인 객체 관리의 문제

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

해결을 위한 3가지 패턴

1. Aggregate(집합체) : Domain 캡슐화. 접근 Root

2. Factory : Domain 객체(또는 Aggregate) 생성 캡슐화

3. Repository : 인프라스트럭쳐 캡슐화 및 객체지향적 접근 제공


Aggregate(집합체)

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

불변식 : 데이터가 변경될 때마다 유지돼야 하는 일관성 규칙

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

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

지역 및 전역 식별성과 객체 참조

그림 2) 지역 및 전역 식별성과 객체참조

참조 : http://www.moshesty.com/category/ddd/

Aggregate 구현 규칙

  • 루트 Entity는 전역 식별성을 지니며, 궁극적으로 불변식을 검사할 책임이 있다.
  • 각 루트 Entity는 전역 식별성을 지닌다. 경계 안의 Entity는 지역 식별성을 지니며, 이러한 지역 식별성은 해당 Aggregate 안에서만 유일하다.
  • Aggregate 경계 밖에서는 루트 Entity를 제외한 Aggregate 내부의 구성요소를 참조할 수 없다.
  • 데이터베이스 질의(Query)를 이용하면 Aggregate의 루트만 직접적으로 획득할 수 있다.
  • Aggregate 안의 객체는 다른 Aggregate의 루트만 참조할 수 있다.
  • 삭제 연산은 Aggregate 경계 안의 모든 요소를 한 번에 제거해야한다.
  • Aggregate 경계 안의 어떤 객체를 변경하더라도 전체 Aggregate의 불변식은 모두 지켜져야 한다.

다시 정리하면

Entity와 Value Object를 Aggregate로 모으고 각각에 대해 경계를 정의하라. 한 Entity를 골라 Aggregate의 루트로 만들고 Aggregate 경계 내부의 객체에 대해서는 루트를 거쳐 접근할 수 있게 하라. Aggregate 밖의 객체는 루트만 참조할 수 있게 하라. 내부 구성요소에 대한 일시적인 참조는 단일 연산에서만 사용할 목적에 한해 외부로 전달될 수 있다. 루트를 경유하지 않고 Aggregate의 내부를 변경할 수 없다. 이런 식으로 Aggregate의 각 요소를 배치하면 Aggregate 안의 객체와 전체로서의 Aggregate의 상태를 변경할 때 모든 불변식을 효과적으로 이행할 수 있다.

Factory

도메인 객체 생성과 Aggregate에 Root 캡슐화 + 불변식 확보

복잡한 객체 생성의 책임을 갖는다.

Factory와 Factory의 위치 선정

  • 객체 내인 경우 팩토리 메소드 패턴
  • 객체 외인 경우 추상 팩토리 패턴

생성자 만으로 충분한 경우

도메인 객체 생성이 복잡하지 않은 경우

인터페이스 설계

팩토리 내의 도메인 객체 메소드 서명(Signature)를 정하는 두가지 사항

  • 각 연산은 원자적이여야한다.
    • 복잡한 도메인 객체를 생성하는 데 필요한 것들은 모두 한 번에 Facotry로 전달해야 한다.
    • 예외가 발생할 시 실패에 대한 표준이 있어야 한다. (null or Exceptoin 등)
  • Factory는 자신에게 전달된 인자와 결합될 것이다.
    • Factory는 당연히 생상할 도메인 객체와 필요한 파라메터와 강결합은 당연하다. 그로 인해 Factory를 이용하는 객체들의 의존성은 약해진다.

불변식 로직의 위치

Facotry의 책임 중 하나는 생성된 객체(Aggregate 포함) 불변식을 충족되도록 보장

고로 Factory가 가지고 있어야한다. 또한 이로 인한 도메인 객체는 본연의 도메인 로직에 응집력을 더 가진다.

Entity Factory와 Value Object Facotry

Value Object

  • 모든 구성요소가 필요

Entity

  • 모든 세부사항이 요구되지 않는다.
  • 하지만 식별자 할당에 대해서 생각해야한다.
    • 사용자로 부터 입력 (ex:자연키)
    • 인프라스트럭쳐 제공 (ex:DB sequence)
    • 다른 객체로 부터 제공

저장된 객체의 재구성

  1. 재구성에 사용된 Entity Factory는 새로운 ID를 사용하지 않는다.
  2. 객체를 재구성하는 Factory는 불변식 위반을 다른 방식으로 처리할 것이다. ??

Repository

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

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

  • 객체 재구성 의 책임
  • 영속화 객체 접근의 캡슐화

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

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

Repository의 이점

  • Repository는 영속화된 객체를 획득하고 해당 객체의 생명주기를 관리하기 위한 단순한 모델을 클라이언트에게 제시한다.
  • Repository는 영속화 기술과 다수의 데이터베이스 전략, 또는 심지어 다수의 데이터 소스로부터 애플리케이션과 도메인 설계를 분리한다.
  • Repository는 객체 접근에 관한 설계 결정을 전해준다.
  • Repository를 이용하면 테스트에서 사용할 가짜 구현을 손쉽게 대처할 수 있다.

Repository에 질의하기

명세(Specification) 패턴 이용을 추천

클라이언트 코드가 Repository 구현을 무시한다. (개발자는 그렇지 않지만)

의도하지 않는 행위가 발생하지 않도록 해야한다. 세부 구현까지는 아니더라도 원리는 파악해야한다.

  • 코스트가 높아져서 성능에 이슈가 생김
  • 예외 발생
  • 인프라스트럭쳐 특성에 따른 사이드이펙트

Repository 구현

  • 도메인 객체 타입을 추상화 한다. (OCP)
  • 클라이언트와 분리를 활용한다. (DIP)
  • 트랜젝션 제어를 클라이언트에 둔다.

프레임워크의 활용

직접 구현은 힘들다. 최근의 언어는 Repository를 구현하는 많은 수단을 제공한다.

ORM 기술이 대표적이다.

Factory와의 관계

객체 생성이나 재구성이 필요할 시 Factory에 해당 책임을 위임하는 것이 좋다.

RDBMS를 위한 객체 설계

데이터 모델과 객체 모델이 서로 갈라지게 해서는 안된다.

위 조건을 만족하기 위해서 필요하다면 일부 객체 관계의 풍부함을 희생해서 관계 모델에 밀접하게 해야한다.

반대로 객체 매핑을 단순화 하는데 도움이 된다면 DB 반정규화를 이용해서 절충한다.

가능하면 원칙을 지키는데 노력을 하고 더 나은 도메인 설계를 위한 경우에만 타협한다. 하지만 원칙없이 단순한 패턴에 의한 절충 오히려 독으로 돌아온다.

MJ

MJ
Backend 개발자 사람입니다. 어플리케이션의 복잡성을 다루는 DDD에 관심이 많습니다. 어제보다 더 나은 개발자가 되려고 항상 노력합니다.

spring boot 2.4.x 에서 openfeign + hystrix 통합하기

spring-boot 2.4.x spring-cloud 2020.x 의존성 상황에서 feign.hystrix.enabled=true가 안됨`feign.circuitbreaker.enabled=true` 로 바꿔보지만 openfeign과 hystr...… Continue reading

IDDD 14장. 애플리케이션

Published on June 19, 2018