CHAPTER 3. 애그리거트
1. 애그리거트
- 복잡한 도메인을 이해하고 관리하기 쉬운 단위로 만들기 위해 상위 수준에서 모델을 조망할 수 있는 방법이다.
- 애그리거트는 관련된 객체를 하나의 군으로 묶어준다.
- 애그리거트 단위로 일관성을 관리하기 때문에 복잡한 도메인을 단순한 구조로 만들어주며, 복잡도가 낮아지는 만큼 도메인 기능을 확장하고 변경하는데 필요한 노력도 줄어든다.
- 한 애그리거트에 속한 객체는 다른 애그리거트에 속하지 않는다. 애그리거트는 독립된 객체 군이며, 각 애그리거트는 자기 자신을 관리할 뿐 다른 애그리거트를 관리하지 않는다.
2. 애그리거트 루트
- 애그리거트에 속한 모든 객체가 일관된 상태를 유지하려면 애그리거트 전체를 관리할 주체가 필요한데 이 책임을 지는 것이 바로 애그리거트의 루트 엔티티이다.
- 애그리거트 루트의 핵심 역할은 애그리거트의 일관성이 깨지지 않도록 하는것이다.
- 애그리거트 루트가 아닌 다른 객체가 애그리거트에 속한 객체를 직접 변경하면 안된다.
불필요한 중복을 피하고 애그리거트 루트를 통해서만 도메인 로직을 구현하게 만들려면 도메인 모델에 대해 다음의 두가지를 습관적으로 적용해야 한다.
- 단순히 필드를 변경하는 set 메서드를 공개(public) 범위로 만들지 않는다.
- 밸류 타입은 불변으로 구현한다.
트랜잭션
- 트랜잭션 범위는 작을수록 좋다.
- 한 트랜잭션에서 한 애그리거트만 수정한다는 것은 애그리거트에서 다른 애그리거트를 변경하지 않는다는 것을 뜻한다.
- 한 애그리거트에서 다른 애그리거트를 수정하면 결과적으로 2개의 애그리거트를 한 트랜잭션에서 수정하게 되므로 한 애그리거트 내부에서 다른 애그리거트의 상태를 변경하는 기능을 실행하면 안된다.
- 만약 부득이하게 한 트랜잭션으로 2개이상의 애그리거트를 수정해야 한다면 애그리거트에서 다른 애그리거트를 직접 수정하지 말고 응용 서비스에서 두 애그리거트를 수정하도록 구현해야 한다.
3. 리포지터리와 애그리거트
- 애그리거트는 개념상 완전한 한 개의 도메인 모델을 표현하므로 객체의 영속성을 처리하는 리포지터리는 애그리거트 단위로 존재한다.
- ex) Order 애그리거트와 관련된 테이블이 3개라면, 리포지토리를 통해서 Order 애그리거트를 저장할 때 애그리거트 루트와 매핑되는 테이블뿐만 아니라 애그리거트에 속한 모든 구성요소를 위한 테이블에 데이터를 저장해야 한다.
4. ID를 이용한 애그리거트 참조
- 애그리거트의 관리 주체가 애그리거트 루트이므로 애그리거트에서 다른 애그리거트를 참조한다는 것은 애그리거트의 루트를 참조한다는 것과 같다.
- JPA를 사용하면 @ManyToOne, @OneToOne 과 같은 어노테이션을 이용해서 연관된 객체를 로딩하는 기능을 제공하고 있으므로 필드를 이용해서 다른 애그리거트를 쉽게 참조할 수 있다.
필드를 이용한 참조 문제점
-
편한 탐색 오용
다른 애그리거트에 쉽게 접근할 수 있기 때문에 에그리거트간의 결합도를 높일 수 있다.
-
성능에 대한 고민
애그리거트를 직접 참조하면 지연로딩과 즉시로딩간의 성능고민을 해야한다.
-
확장
서로 다른 DBMS를 사용하는 경우 단일기술로는 확장하기 어렵다.
위의 문제를 완화할 때 사용할 수 있는것이 ID를 이용해 다른 애그리거트를 참조하는 것이다.
- ID를 이용한 참조는 DB 테이블에서 외래키를 사용해서 참조하는것과 비슷하지만, 애그리거트 내의 엔티티를 참조할때는 객체 레퍼런스로 참조한다.
- ID참조를 사용하면 모든 객체가 참조로 연결되지 않고, 한 애그리거트에 속한 객체들만 참조로 연결된다.
- 이를 통해 응집도가 높아지고, 결합도도 낮아지며, 구현 복잡도도 낮아진다. 또한 한 애그리거트에서 다른 애그리거트를 수정하는 문제를 원천적으로 방지할 수 있다.
- 외부 애그리거트를 직접 참조하지 않기 때문에 애초에 한 애그리거트 내에서 다른 애그리거트의 상태를 변경할 수 없다.
- 애그리거트별로 다른 구현기술을 사용하는것도 가능해지기 때문에 확장성이 높아진다.
- N+1 조회와 같은 성능문제가 발생하는 경우는 JPQL이나 Mybatis와 같은 기술을 이용해서 해결할 수 있다.
5. 애그리거트 간 집합 연관
- 1 : N의 관계인경우 N에 해당하는 데이터가 많아 성능에 심각한 오류가 발생할 경우가 있다. 이런 경우 N : 1의 관계로 변경하여 해결하는데, 코드로써는 N에게 1에 해당하는 연관을 추가해서 거꾸로 조회함으로써 문제를 해결할 수 있다.
6. 애그리거트를 팩토리로 사용하기
- 애그리거트가 갖고 있는 데이터를 이용해서 다른 애그리거트를 생성해야 한다면 애그리거트에 팩토리 메서드를 구현하는것을 고려해보자.
- 팩토리 메서드를 추가하면 생성시 필요한 데이터의 일부를 직접 제공하면서, 동시에 중요한 도메인 로직을 함께 구현할 수 있게 된다.