도메인 주도 개발을 공부하며, 앞에서 잠깐 다루었던 애그리거트에 대해
자세하게 알아보자.
DB에서 백 개 이상의 테이블을 한 장의 ERD에 모두 표시한다면 어떨까?
테이블 간의 관계 파악도 매우 힘들 것이고, 데이터 구조를 큰 틀에서
보았을 때 이해하는 것도 굉장히 어려울 것이다.
복잡한 도메인을 이해하기 위해서 관리하기 쉬운 단위로 만들려면
상위 수준에서 모델들을 파악하는 방법이 있어야한다.
그 방법이 바로 애그리거트이다!
많은 객체들을 애그리거트로 묶어 보면 상위에서 도메인 모델 간
관계들을 쉽게 파악할 수 있다.
애그리거트는 모델 이해 뿐만 아니라, 일관성 관리에도 도움을 준다.
복잡한 도메인을 단순한 구조로 만들어주며, 그만큼 기능을 확장/변경할 때
개발 시간도 줄어들어 훨씬 효율적이다.
애그리거트에 소속되어있느 모든 객체는 정상이어야 한다.
모든 객체가 일관된 상태를 유지하려면 전체를 관리할 주체가 필요하다.
이를 책임지는 것이 애그리거트의 루트 엔티티이다.
예를 들어, 주문 애그리거트에서 루트 역할을 하는 엔티티는 Order이다.
OrderLine, ShippingInfom, Orderer 등 주문 애그리거트에 속한
모델은 Order에 직접 또는 간접적으로 속하기 때문이다.
애그리거트 루트가 하는 일은 일관성 유지이다.
애그리거트 외부에서 애그리거트에 속한 객체를 직접 변경하거나
하는 등의 일관성을 깨는 코드를 방지해야한다.
한 트랜잭션이 한 개의 테이블을 수정하는 것과 세 개의 테이블을
수정하는 것을 비교하면 성능에서 차이가 발생한다.
따라서 트랜잭션 범위는 작을 수록 좋다.
한 애그리거트에서 다른 애그리거트를 수정하면 두 가지의 애그리거트를
한 트랜잭션에서 수정하는 것이 된다.
애그리거트는 개념상 완전한 한 도메인 모델을 표현하기 때문에,
객체의 영속성을 처리하는 리포지터리는 애그리거트 단위로 존재한다.
따라서 애그리거트 루트에만 리포지터리가 존재한다.
한 객체가 다른 객체를 참조하는 것처럼, 애그리거트도 그렇다.
다른 애그리거트 루트를 필드로 참조할 수도 있다.
이는 개발자에게 구현의 편리함을 제공한다.
하지만 애그리거트 참조는 다음 문제를 야기할 수 있다.
애그리거트 내부에서 타 애그리거트 객체에 접근할 수 있으면,
다른 애그리거트의 상태를 쉽게 변경할 수 있게 된다.
구현의 편리함 때문에 다른 애그리거트를 수정하면 트랜잭션 범위도 증가하게된다.
그리고 애그리거트를 직접 참조하면 성능이 매우 떨어진다.
따라서 ID를 이용해 이를 필드로 만들어 참조할 수 있다.
다른 애그리거트를 참조하지도 않아 로딩에 대해 고민하지 않아도 된다.
허나 ID로 참조하면 조회에 있어 한 가지 문제점이 발생한다.
예를들어 주문 개수가 10개면, 주문을 읽어오는 1번의 쿼리와 상품을 읽어오는
10개의 쿼리가 실행된다. 이를 N+1 조회 문제라고 한다.
이는 더 많은 쿼리를 실행해 전체 조회 속도가 느려지는 원인이 된다.
이를 해결하려면 조회 전용 별도 DAO를 만들고 조인을 하면 된다.
1-N 관계일 때는 N이 연관관계의 주인이다.
M-N 일 때는 JoinTable으로 연관관계를 적용할 수 있다.
애그리거트의 장점에 대해 배워보았는데, 굉장히 좋은 개념인 것 같다.
말로만 듣던 일관성 유지와 N+1 조회 문제와 관련된 여러가지 개념들을
배워보아서 유익했다!