3. 애그리거트
제3장 애거리거트
Last updated
Was this helpful?
제3장 애거리거트
Last updated
Was this helpful?
상위 수준에서 모델을 정리하면 복잡한 도메인 모델의 관계를 이해하는데 도움이 된다.
복잡한 도메인을 이해하고 관리하기 쉬운 단위로 만들어 상위 수준에서 모델을 조망할 수 있는 방법이 애그리거트이다.
애그러거트는 관련된 객체를 하나의 군으로 묶어준다.
수 많은 객체를 애그리거트로 묶어서 바라보면 좀 더 상위 수준에서 도메인 모델 간의 관계를 파악할 수있다.
73p 그림3.3을 보면 복잡했던 그림3.2를 애그리거트로 묶음으로 인하여 모델 간의 관계를 개별 모델 수준과 함께 상위 수준에서도 이해할 수 있게 된다.
애그리거트는 모델을 이해하는데 도움을 줄 뿐만 아니라 일관성을 관리하는 기준이 된다.
애그리거트의 객체들은 유사하거나 동일한 라이프사이클을 가지게 된다.
따라서 애그리거트에 속한 구성요소는 대부분 함께 생성하고 함께 제거한다.
한 애그리거트에 속한 객체는 다른 애그리거트에 속하지 않는다.
애그리거트는 독립된 객체군이며, 각 애그리거트는 자기 자신을 관리할 뿐 다른 애그리거트를 관리하지 않는다.
경계를 설정할 때 기본이 되는 것은 도메인 규칙과 요구사항이다.
도메인 규칙에 따라 함께 생성되는 구성요소는 한 애그리거트에 속할 가능성이 높다.
함께 변경되는 빈도가 높은 객체는 한 애그리거트에 속할 가능성이 높다.
예외사항 - 75p 그림3.4와 같이 삼품과 리뷰가 같이 표출되어야 한다는 요구사항이 존재 할 경우 상품과 리뷰는 같은 애그리거트로 이해 할 수 있지만, 상품과 리뷰는 함께 생성되지 않을 뿐더라 변경 주체도 다르기 때문에 서로 다른 애그리거트에 속한다.
따라서 예상보다 하나의 엔티티 객체를 갖는 애그리거트가 많으며, 두 개 이상의 엔티티로 구성 되는 애그리거트는 예상보다 많지 않다.
애그리거트는 여러 객체로 구성되기 때문에 한 객체만 상태가 정상이서는 안 된다. 도메인 규칙을 지키려면 애그리거트에 속한 모든 객체가 정상 상태를 가져야 한다.
애그리거트에 속한 모든 객체가 일관된 상태를 유지하려면 애그리거트 전체를 관리할 주체가 필요한데 이 책임을 지는 것이 애그리거트의 루트 엔티티이다.
애그리거트의 루트 엔티티는 대표로써 애그리거트의 속한 객체는 루트 엔티티에 직접 또는 간접적으로 속한다.
애그리거트 루트는 애거리거트에 속한 객체를 포함하는 것으로 끝나는 것이 아닌 애그리거트 루트의 핵심 역활은 애그리거트의 일관성이 깨지지 않도록 하는 것이다.
예를들어 주문 애그리거트는 배송지 변경, 상품변경과 같은 기능을 제공하는데 루트인 주문 애그리거트가 이 기능을 구현 한 메서드를 제공한다.
아래는 애그리거트 루트인 Order에서 ShippingInfo를 가져와 직접 정보를 변경하고 있다.
애그리거트 루트가 아닌 다른 객체가 애그리거트에 속한 객체를 직접 변경하면 안된다.
데이터의 일관성이 깨지고 일관성을 지키기위해 응용 서비스에 벨리데이션 체크를 할 수 있지만 동일한 로직이 여러 응용서비스에 중복해서 구현 될 가능성이 크다.78p상단 소스 참고
불필요한 중복을 피하고 애그리거트 루트를 통해서만 도메인 로직을 구현하게 만들려면 아래 두가지를 습관적으로 적용해야한다.
단순히 필드를 변경하는 set 메서드를 공개 범위로 만들지 않는다.
벨류 타입은 불변으로 구현한다.
벨류 타입을 불변으로 만들어 상태를 변경할 수 없게하면 위와 같이 직접 변경할 경우 컴파일 에러가 난다.
불변인 벨류타입을 변경하려면 새로운 벨류 객체를 전달하여 변경한다.
애그리거트 루트는 애그리거트 내부의 다른 객체를 조합해서 기능을 완성한다.
예를들어 Order는 총 주문 금액을 구하기 위해 OrderLine 목록을 사용한다.
트랜잭션의 범위는 작을수록 좋다.
한 트랜잭션에서는 한개의 애그리거트만 수정해야한다.
한 트랜잭션에서 두 개 이상의 애그리거트를 수정하면 트랜잭션 충돌이 발생할 가능성이 더 높아진다.
한 트랜잭션에서 한 애그리거트만 수정한다는 것은 애그리거트에서 다른 애그리거트를 변경하지 않는 다는것을 뜻한다.
한 애그리거트가 다른 애그리거트의 기능에 의존하면 애그리거트 간 결함도가 높아져 향후 수정 비용이 증가하기 때문에 애그리거트에서 다른 애그리거트의 상태를 변경하지 말아야한다,
부득이하게 한 트랜잭션으로 두 개 이상의 애그리거트를 수정해야 한다면 애그리거트에서 다른 애그리거트를 수정하지 말고 응용 서비스에서 두 애그리거트를 수정한다.
한 트랜잭션에서 한 개의 애그리거트를 변경하는 것을 권장하지만 다음의 경우에는 두 개 이상의 애그리거트를 변경하는 것을 고려할 수 있다.
팀표준 - 팀이나 조직의 표준에 따라 관련된 응용 서비스의 기능을 한 트랜잭션으로 반드시 사용해야 하는 경우
기술 제약 - 한 트랜잭션에서 두 개 이상의 애그리거트를 수정하는 대신 도메인 이벤트와 비동기를 사용하는 방식을 사용하는데 기술적으로 그렇게 하지 못 할 경우
UI 구현의 편리 - 운영자의 편리함을 위하여 주문 목록 화면에서 여러 주문 상태를 한번에 변경하고 싶을 경우 한 트랜잭션에서 여러 주문 애그리거트 상태를 변
애그리거트는 개념상 완전한 한 개의 도메인 모델을 표현하므로 객체의 영속성을 처리하는 리포지터리는 애그리거트 단위로 존재한다.
예를들어 Order가 애그리거트 루트이고 OrderLine인 애그리거트에 속하는 구성요소로 Order를 위한 리포지터리만 존재한다.
애거리거트를 영속화하고 애그리거트를 사용하려면 저장소에서 애그리거트를 읽어야 하므로 리포지터리는 적어도 다음의 두 메서드를 제공해야한다.
save - 애그리거트 저장
findById - ID로 애그리거트를 구함
애그리거트는 개념적으로 하나이므로 리포지터리는 애그리거트 전체를 저장소에 영속화 해야한다.
리포지터리가 완전한 애그리거트를 제공하지 않으면 필드나 값이 올바르지 않아 애그리거트의 기능을 실행하는 도중에 NullPointerException과 같은 이슈가 발생한다.
RDMS를 이용해서 리포지터리를 구현하면 트랜잭션을 이용하여 애그리거트의 변경이 저장소에 반영하는 것을 보장할수 있고 NoSQL를 사용할 경우 애그리거트를 한 개 문서에 저장함으로써 보장 할 수 있다.