DDD START SERIES는 도메인 주도 개발 시작하기 책을 참고 하여 작성된 요약 글 입니다.

EP 3.1 애그리거트

공통된 모델을 애그리커트 단위로 묶어서 표현한다. 보기도 쉬워지고 좋다.

상위 수준에서 모델화 모델을 정리하면 도메인의 복잡한 관계가 더 이해가 잘 된다.

애그리거트를 단위로 묶으면 이해하는 것뿐아니라 일관성 관리에 기준이 된다. 복잡도를 줄이고 변경 시간을 줄어들게 만든다.

애그리거트로 묶인 도메인들은 함께 생성하고 함께 제거한다.

주문으로 묶인 집합은 애그리거트는 회원이란 애그리거트의 영역을 침범하지 않는다.

경계를 설정할때 기본은 도메인의 규칙과 요구사항이다.

값이 같이 변경되는 경우는 같은 애그리거에도에 속할 가능성이 높다

처음 도메인을 만들면 큰애그리거트로 보이는 것이 많지만 어느정도 도메인에 대한 이해가 쌓이면 애그리지트의 실제 크기는 줄어든다.

애그리거모를 잘 쪼개 묶어야 유지회에 용이하다. 애그리워트는 같은 역할 군집에 넣을 수 있다.

EP 3.2 애그리커트 루트

애그리거트는 여러 객체로 구성된다.

그렇기에 한 객체만 상태가 정상이면 안 되고 모두가 정상이여야 한다.

3.2.1 도메인 규칙과 일관성

애그리거트 루트는 애그리거트의 일관성이 깨지지 않도록하는것이다.

이를 위해 애그리거트 루트는 애그리거트가 해야할 도메인 기능을 구현한다.

애그리거트 루트가 제공하는 메서드는 도메인규칙에 따라 애그리거트에 속한 객체의 일관성이 깨지지 않도록 구현한다.

애그리거트 외부에서 애그리거트에 속한 객체를 직접 변경하면 안된다.

이것은 애그리거트 루트가 강제하는 규칙을 적용할 수 없어 모델의 일관성을 깬다.

불필요한 중복을 피하고. 애그리거트 루트를 통해서만 도메인 로직을 구현하게 만들기 위해 두가지 습관이 필요하다.

  1. set 메서드는 public으로 만들지 마라.
  2. 벨류 타입은 불변으로 만든다. (불변타입은 외부에서 수정이 불가하다.)

그렇기에 변경할수 있는 방법은 새로운 벨류 객체를 생성하는 것 뿐이다.

그럼 결국 애그리거트 루트를 써야 하기에 객체의 일관성이 유지된다.

3.2.2 애그리거트 루트의 기능 구현

애그리거트 루트는 애그리거트를 조합해서 기능을 완성하게 된다.

애그리거트 루트에서 조합을해 기능을 만들고 외부에서 가져다 쓰는 방식으로 사용하도록 하자.

만약 외부에서도 사용하고 싶지 않다면 접근제어자를 protected로 선언하여 막아보자.

3.2.3 트랜잭션의 범위

트랜잭션의 범위는 작을수록 좋다. 트랙잭션당 1개의 테이블로 잡고가는것이 성능에 좋다.

하나의 트랜잭션에는 하나의 애그리거트의 그룹만 있는 있는것이 좋다.

거기다 다른 애그리거트의 기능에 의존하게 되면 코드의 결합도가 증가하고 수정 비용도 증가한다.

부득이하게 한 트랜잭션으로 두개이상의 애그리거트를 써야 하는경우 직접 수정하지 말고 응용 서버스에서 수정하도록 구현한다.

물론 예외는 있다.

ex) 팀 표준, 기술 제약, UI 구현의 편리.

3.3 리포지터리와 애그리거트

애그리거트는 레포지토리와 쌍이다. / 개념적으로 다가.

애그리거트는 개념적으로 하나이므로 리포지터리는 애그리거트 전체를 저장소에 영속화해야한다.

애그리거트 루트 말고 이 전체 그룹의 모든 값을 저장 변경해야한 리포지터리가

만약 완전한 애를 제공하지 않으면 필드나 값이 올바르지 않아 애그리거트의 기능을 실행하는 도중 NPE와 같은 문제가 발생할 수 있다.

•애그리거트는 리모리터리와 쌍을 맺어요킨다. 애려구트의 그룹 전체를 영속화하자.

3.4 3.4 ID를 이용한 애그리커트

다른 애그리커트를 참조한다는 것은 다른 애그리커트 루트를 참조 한다는 것이다.

예로 주문에서 회원을 참조하기 위해 애그리커트 루트 멤버를 필드로 참고 할수있다.

하지만 필드를 이용한 애그리거트 참조는 다음과 같은 3가지의 문제를 야기할 수 있다.

첫번째로 애그리커트를 참조할때 가장 큰 문제는 편리함 때문에 오용할 수 있다.

애그리커트 내부에서 다른 애그리커트 객체에 접근 할 수 있으면 상태를 쉽게 변경할 수 있다.

트랜적션 범위로 애그리커트가 관리하는 범위는 자기 자신으로 해야 한다.

근데 접근 할 수 있으면 다른 애를 수정하고자 하는 유혹에 빠질 수 있고 이러한 결과를 결합도를 높인다.

두번째로 JPA를 사용할때 즉시 로딩의 경우에는 조회 성능에는 유리하지만 상태를 변경하는 기능을 실행하는 경우 에는 불필요한 객체를 함께 로딩하기에 지연 로딩이 유리할 수 있다.

세번째로 확장으로 단일 기술의 문제도 발생할 수 있다. 마지아를 쓰다 몽고를 써서 다 다른 기술을 쓰는 경우가 있을수 있다.

이런 문제들을 완화할때 쓰는 것이 ID값을 이용해 다른 애를 참조하는 것이다.

이는 DB의 외래키와 비슷하다.

객체의 참조만 이루어지면 되기에 이는 경계를 명확하게 하고 결합도를 낮춘다.

애그리커트간의 의존을 제거하므로 응집도를 높여주는 효과가 있으며 구현 복잡도도 또한 낮아진다.

참조가 필요할 경우 응용 서비스 단계에서 ID를 참조하여 로딩하면 애그리거트 수준에서는 지연 로딩과 비슷한 동일한 결과를 얻는다.

ID 참조 방식은 복잡도를 낮추고 다른 애그리거트를 수정하는 문제를 근원적으로 방지할 수 있다.

또한 애그리거트 마다 다른 구현 기술을 사용할 수도 있어 확장이 용이하다.

3.4.1 ID를 이용한 참조와 조회 성능

다른 애그리거트의 ID를 참조하게 되면 참조하는 여러개의 애그리거트를 읽을때 속도에 문제가 생길 수 있다.

N+1의 조회 문제가 발생할 수 있고 이는 후후 많은 쿼리를 실행하기에 전체 조회 속도를 느리게 만든다.

이런 문제를 위해 조인을 사용한다.

하지만 조인은 ID 참조 방식을 객체 참조 방식으로 바꾸기에 이를 해결하기 위해 전용 쿼리를 사용하면 된다.

혹여나 서로 다른 저장소를 사용한다면 조회 성능을 높이기 위해 캐시를 적용하거나 조회 전용 저장소를 따로 구성하도록 한다.

3.5 애그리거트 간 집합 연관

애그리거트의 대표적인 연관 관계로는 1-N, M-N 가 있다.

대표적인 예로 카테고리와 상품으로 예시를 들 수 있는다.

1-N 관계는 Set으로 표현할 수 있다.

하지만 1-N을 set으로 설계해놓으면 상품의 계수가 수만 건일 경우 성능 저하가 발생할 수 있다.

그렇기에 실제 구현에는 반영하지 않고 거꾸로 상품에 카테고리를 구현한다.

M-N은 개념적으로 양쪽 애그리거트에 컬렉션으로 연관을 만든다.

개념에는 양방향으로 존재하지만 실제 구현에는 상품에서 카테고리로의 단방향 M-N 연관만 적용하면 된다.

3.6 에그리거트를 팩토리로 사용하기

상품을 등록할 수 있는 기능을 구현할 때 애그리 거스를 분리하여 Store와 produce를 분리해 Store에서 product를 생성하는 팩토리를 구현할 수 있다.

이렇게 하면 Store의 코드만 변경하면, 응용 서비스는 영향을 받지 않는다.

그로 인해 도메인의 응집 도도 높아진다. 이것이 애그리거트를 팩토리로 사용할 때의 장점이다.

애그리거트 안에서 다른 애그리거트를 생성해야 할 때 팩토리 메서드를 활용해 보자.

그러면 다른 애그리거트를 생성할 때 필요한 데이터를 직접 제공하면서 동시에 중요한 도메인 로직을 함께 구현할 수 있다.

dd

REFERENCE

itssobeauti 도메인 주도 개발 시작하기