/////
Search
Duplicate
2️⃣

애그리거트 루트

: 주문 애그리거트는 다음을 포함
- 총 금액인 totalAmounts를 갖고 있는 Order 엔티티
- 개별 구매 상품의 개수인 quantity와 금액인 price를 갖고 있는 OrderLine 밸류
: 구매할 상품의 개수를 변경하면 한 OrderLinequantity를 변경하고 더불어 OrdertotalAmounts도 변경해야 한다. 그렇지 않으면 다음 도메인 규칙을 어기고 데이터 일관성이 깨지게 된다.
- 주문 총 금액은 개별 상품의 주문 개수 * 가격의 합
: 애그리거트는 여러 객체로 구성되기 때문에 어느 하나라도 상태가 정상이지 않으면 안 된다.
도메인 규칙을 지켜 애그리거트 내의 모든 객체가 정상 상태를 가져야한다.
주문 애그리거트에서 OrderLine을 변경하면 OrdertotalAmounts도 다시 계산해서 총 금액이 맞아야 한다.
: 애그리거트에 속한 모든 객체가 일관된 상태를 유지하려면 애그리거트 전체를 관리할 주체가 필요한데, 이런 책임을 지고 있는 것이 바로 애그리거트의 루트 엔티티다.
애그리거트 루트 엔티티는 애그리거트의 대표 엔티티다. 애그리거트에 속한 객체는 애그리거트 루트 엔티티에 직접 또는 간접적으로 속하게 된다.
ex) 주문 애그리거트에서 루트 역할을 하는 엔티티는 Order, OrderLine, ShippingInfo, Orderer 등 주문 애그리거트에 속한 모델은 Order에 직접 또는 간접적으로 속한다.

1. 도메인 규칙과 일관성

: 애그리거트 루트가 단순히 애그리거트에 속한 객체를 포함하는 것으로 끝나는 것은 아니며 핵심 역할은 일관성이 깨지지 않도록 하는 것이다.
: 이를 위해 애그리거트 루트는 애그리거트가 제공해야 할 도메인 기능을 구현
ex) 주문 애그리거트는 배송지 변경, 상품 변경과 같은 기능을 제공하고 애그리거트 루트인 Order가 이 기능을 구현한 메서드를 제공한다.
: 애그리거트 루트가 제공하는 메서드는 도메인 규칙에 따라 애그리거트에 속한 객체의 일관성이 깨지지 않도록 구현해야 한다.
ex) 배송이 시작되기 전까지만 배송지 정보를 변경할 수 있다는 규칙이 있다면, 애그리거트 루트인 Order의 changeShippingInfo() 메서드는 이 규칙에 따라 배송 시작 여부를 확인하고 규칙을 충족할 때만
배송지 정보를 변경해야 한다.
export class Order { private readonly shippingInfo: ShippingInfo; public changeShippingInfo(shippingInfo: ShippingInfo) { verifyNotYetShipped(); setShippingInfo(shippingInfo); } public verifyNotYetShipped() { if (state != OrderState.PAYMENT_WAITING && state != OrderState.PREPARING) throw new IllegalStateException("alreay shipped"); } ... }
TypeScript
복사
⇒ 애그리거트 외부에서 애그리거트에 속한 객체를 직접 변경하면 안 된다, 이것은 애그리거트가 강제하는 룰을 깨치는 행위이므로
: 불필요한 중복을 방지하고 애그리거트 루트를 통해서만 도메인 로직을 구현하게 만들려면 도메인 모델에 대해 다음의 두 가지를 습관적으로 적용해야 한다.
단순히 필드를 변경하는 set 메서드를 public 제한자로 생성하지 않는다.
⇒ 외부에서 내부 상태를 함부로 변경할 수 없으므로 애그리거트의 일관성이 깨질 가능성이 줄어든다.
밸류 타입은 불변으로 구현한다.
⇒ 이렇게 했을 경우, 객체의 값을 변경하는 방법은 새로운 밸류 객체를 할당하는 것뿐으로 간접적으로 의존하고 있는 밸류 타입의 일관성을 깨지 않을 수 있다.

2. 애그리거트 루트의 기능 구현

: 애그리거트 루트는 애그리거트 내부의 다른 객체를 조합해서 기능을 완성한다.
: 애그리거트 루트가 구성요소의 상태만 참조하는 것뿐만 아니라 기능 실행을 위임하기도 한다.
ex) 예를 들어 구현 기술의 제약이나 내부 모델링 규칙 때문에 OrderLine 목록을 별도 클래스로 분리했을 때, 이 경우 OrderchangeOrderLine() 메서드는 다음과 같이 내부 orderLines 필드에 상태
변경을 위임하는 방식으로 기능을 구현한다.

3. 트랜잭션 범위

: 트랜잭션의 범위는 작을수록 좋다, 한 트랜잭션이 한 테이블을 수정하는 것과 세 테이블을 수정하는 것을 비교하면 성능에서 차이가 발생하기 때문, 한 개 테이블을 수정하면 트랜잭션 충돌을 막기 위해
락 걸리는 대상이 한 개 테이블의 한 행으로 한정되지만, 세 개의 테이블을 수정하는 것은 그 락의 대상이 더 많아진다. 잠금 대상이 많아진다는 것은 그만큼 동시에 처리할 수 있는 트랜잭션 개수가
줄어든다는 것을 의미하고 이것은 전체적인 성능을 떨어뜨린다.
: 한 트랜잭션에서 한 애그리거트만 수정한다는 것은, 다른 애그리거트에 변경이 발생하지 않는다는 것을 의미, 한 애그리거트에서 다른 애그리거트를 수정하면 결과적으로 두 개의 애그리거트를 한
트랜잭션에서 수정하게 되므로, 애그리거트 내부에서 다른 애그리거트의 상태를 변경하는 기능을 실행하면 안 된다.
: 이것은 애그리거트가 자신의 책임 범위를 넘어서 다른 애그리거트의 상태까지 관리하는 꼴이 되므로 추후 유지보수 과정에서 이슈가 발생할 가능성이 존재한다. 이는 애그리거트 간의 결합도를 높여 향후
유지보수 비용을 높게 만드므로 애그리거트는 서로 최대한 관심사를 분리하여 독립적으로 작업을 처리해야 한다.
: 부득이하게 한 트랜잭션으로 두 개 이상의 애그리거트를 수정해야 한다면 애그리거트에서 다른 애그리거트를 직접 수정하지 말고 응용 서비스에서 두 애그리거트를 수정하도록 구현한다.
: 도메인 이벤트를 사용하면 한 트랜잭션에서 한 개의 애그리거트를 수정하면서 동기나 비동기로 다른 애그리거트의 상태를 변경하는 코드를 작성할 수 있다.
: 물론 한 트랜잭션에서 한 개의 트랜잭션을 변경하는 것을 권장하나 다음과 같은 경우에는 두 개 이상의 애그리거트를 변경하는 것을 고려할 수 있다.
팀 표준: 팀이나 조직의 표준에 따라 사용자 유스케이스와 관련된 응용 서비스의 기능을 한 트랜잭션으로 실행해야 하는 경우
기술 제약: 기술적으로 이벤트 방식을 도입할 수 없는 경우, 한 트랜잭션에서 다수의 애그리거트를 수정해서 일관성을 처리해야 한다.
UI 구현의 편리: 운영자의 편리함을 위해서 주문 목록 화면에서 여러 주문의 상태를 한 번에 변경하고 싶을 때, 한 트랜잭션에서 일관적으로 처리해야 한다.