#03장 애그리거트
최범균 저「DDD START!, 2016」를 읽고 정리하였습니다.
다룰 내용
애그리거트
애그리거트 루트와 역할
애그리거트와 리포지터리
ID를 이용한 애그리거트 참조
애그리거트
복잡한 객체관계에 대한 이해보다, 상위 수준에서 도메인 모델 관계를 이해해야 된다.
그게 바로 애그리거트
동일한 애그리거트에 있는 객체는 동일한 라이프사이클을 갖는다.
Order, OrderLine, Orderer은 함께 생성된다.
ShippingInfo에서 주문자를 생성하지 않는다.
주문 애그리거트에서 회원 비밀번호를 변경하거나 상품의 가격을 변경하지 않는다.
경계 설정에는, 도메인 규칙과 요구사항이 기본이다.
주문시점 생성되는 애그리거트 = 주문 상품 개수, 배송지 정보, 주문자 정보
A가 B를 갖는다고 한 애그리거트가 아니다
예, 상품 - 리뷰 (상품이 생성된다고 리뷰가 생성되는 것은 아니다)
경험상, (대부분) 한 애그리거트에는 한개의 엔티티 객체만 갖는다.
애그리거트 루트와 역할
모든 객체가 일관된 상태를 유지하기 위해, 전체를 관리할 주체 “애그리거트 루트 엔티티”가 있다.
루트 역할: 일관성이 깨지지 않게, 중앙적으로 관리한다.
예, 주문 애그리거트는 배송지 변경, 주문 상품 변경 기능 제공
이를 달성하기 위한 팁
set 메서드는 public 범위로 만들지 않는다.
밸류타입은 불변으로 구현한다.
외부에서 밸류 객체의 상태를 변경할 수 없게 된다. (루트에서만 관리)
트랜잭션 범위는 작게하자. ( 성능적으로, 테이블 3개를 잠그는 것보다 1개만 잠그는게 훨씬 좋음)
한 트랜잭션에서 한 개의 애그리거트만 수정한다. (충돌 낮춤)
다른 말로, 2개의 애그리거트 함께 수정되는 트랜잭션 자제
예, 주문 중에서 배송지를 변경했다고, 회원 자체의 배송지 정보를 변경하지 말자. (주문, 회원 애그리거트 손대는 행위) 결합도는 최대한 낮추자.
그러나, 책에선 3가지 제약의 이유로 두개 애그리거트 변경이 발생할 수 있다고 이야기한다.
2개의 애그리거트를 건들시, 한 애그리거트에서 다른 애그리거트를 건드리는게 아니라, 응용 서비스에서 처리하자.
애그리거트와 리포지터리
리포지터리는 애그리거트 단위로 존재한다.
애그리거트는 개념적으로 하나이므로, 전체를 저장소에 영속화 한다.
ID를 이용한 애그리거트 참조
다른 애그리거트를 참조한다는 것은 애그리거트 루트를 참조한다는 의미이다.
예, 주문 애그리거트의 Orderer에서 회원은 멤버 애그리거트 루트인 Member를 참조한다.
ORM 덕에, 애그리거트 루트 참조 쉽다.
하지만 3가지 문제점
편한 탐색 오용, 성능에 대한 고민점, 확장 어려움
해결방법: ID를 이용한 다른 애그리거트 참조
해결방법 예시 - Member를 MeberId로 바꾸자.
변경 전

변경후
주문이랑 멤버가 아니라도, 다른 도메인이 있다고 가정하면, 다른 DB를 사용할 수 있기 때문에 이렇게 하면 ~

다음과 같이 사용할 수 있따.
하지만... 조회성능에서 문제가 찾아올 수 있다. (예, N+1 문제)
이를 해결하기 위해, 전용 조회 쿼리 사용하자.
별도의 조회용 DAO 를 만든다.
아래는 사용자 주문내역인 OrderView를 만든다. 주문, 멤버, 상품 애그리거트를 세타조인 했다.
자세한 조회전용 쿼리는 5장에서
애그리거트 간 집합 연관
애그리거트 간의 1:N 연관을 실제 구현에 반영하는 경우는 드물다.
솔직히 아래 사례, 무슨말인지 잘 모르겠다.. 카테고리 객체 코드에서 상품 가져오는 코드가 페이징 처리를 상품을 다 가져오고 나서 해서... 비교가 되는 코드인지 모르겠다.
예, 카테고리 - 상품 (상품은 하나의 카테고리에만 들어간다 가정)
카테고리 객체에 Set으로 상품들 담아두면, 특정 카테고리의 상품들을 조회시 성능 문제 발생
해결을 위해, 1:N이 아닌 N:1의 입장에서 생각하자.
상품에 카테고리 ID를 넣어두고, 페이징처리하면서 부분만 가져오자.
M:N 의 경우, 단방향으로만 적용하면 된다.
애그리거트를 팩토리로 사용하기
도메인 로직을 응용서비스에 노출하지 말자.
Last updated