Lined Notebook

[DDD] Aggregate

by HeshAlgo

DDD

1. Aggregate(애그리거트)?

- 관련된 도메인을 하나의 군집으로 묶어주는 것

 

도메인 모델이 복잡해졌을 때, 개별 객체 수준에서 모델을 바라보면 전반적인 구조나 큰 수준에서 도메인간의 관계를 이해하기 어려워집니다. 주요 도메인 개념간의 관계를 파악하기 어렵다는 것은 코드를 변경하고 확장하는 것이 어려워진다는 것을 의미합니다.

이런 복잡한 도메인을 이해하고 관리하기 쉬운 단위로 관리할 수 있는 방법이 필요한데 그 방법이 바로 애그리거트이다.

  • 일관성을 관리하기 떄문에 복잡한 도메인 구조를 단순한 구조로 만들어줌
  • 도메인 기능을 확장하고 변경하는데 필요한 시간도 줄어듬
  • 애그리거트에 속한 객체는 유사하거나 동일한 라이프 사이클을 갖음
  • 애그리거트에 속한 구성요소는 대부분 함께 생성하고 함께 제거
    -> 주체가 다르면 같은 애그리거트로 보기 힘듬
    -> ex) 상품 / 리뷰로 봤을때 상품이 생긴다고 해서 리뷰도 함께 생성되는 것이 아니다.

2. 애그리거트 루트

- 애그리거트에 속한 모든 객체가 일관된 상태를 유지하기 위해 애그리거트 전체를 관리하는 주체

- 애그리거트에 속한 객체는 루트 엔티티에 직접 또는 간접적으로 속한다.

example

  • 총 금액인 totalAmounts를 갖고있는 Order 엔티티
  • 개별 구매 상품의 개수인 quantity와 금액인 price를 갖고 있는 OrderLine 밸류

구매할 상품의 개수(quantity)를 변경하면 총 금액인 totalAmounts도 변경해야한다.

그렇지 않을 경우 도메인 규칙을 어기고 데이터의 일관성이 깨진다.

 

3. 애그리거트 일관성

일관성을 지키기 위해서 2가지를 기억해야 한다.

 

1) set 메서드를 공개(public) 범위로 만들지 않기

-> 중요 도메인의 의미나 의도를 표현하지 못하고 도메인 로직이 Presentation 영역이나 Application 영역으로 분산되게 만드는 원인

Domain 모델의 Entity나 Value에 공개 set메서드만 넣지 않아도 일관성이 깨질 가능성 낮아짐

 

2) 밸류 타입은 불변으로 구현

-> 애그리거트 외부에서 내부 상태를 함부로 바꾸는 것이 불가능함

 

 

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

1) 애그리거트 루트는 애그리거트 내부의 다른 객체를 조합해서 기능을 완성

예시 1) Order는 총 주문 금액을 구하기 위해 OrderLine 목록 사용

public class Order {
    private Money totalAmounts;
    private List<OrderLine> orderLines;
    
    private void calculateTotalAmounts() {
        int sum = orderLines.stream()
                  .mapToInt(ol -> ol.getPrice() * ol.quantity())
                  .sum();
        this.totalAmouts = new Money(sum);
    }
}

예시 2) Member는 암호를 변경하기 위해 Password 객체에 암호가 일치하는지 여부를 확인

public class Member {
    private Password password;
    
    public void changePassword(String currentPassword, String newPassword) {
        if (!password.match(currentPassword)) {
            throw new PasswordNotMatchException();
        }
        this.password = new Password(newPassword);
    }

}

 

2) 애그리거트 루트는 기능 실행을 위한 위임을 한다.

예시 1) 내부의 orderLines필드에 상태 변경을 위임

public class Order {
    private OrderLines orderLines;
    
    public void changeOrderLines(List<OrderLine> newLines) {
        orderLines.changeOrderLines(newLines);
        this.totalAmounts = orderLines.getTotalAmounts();
    }
}

 

 

5. 트랜잭션 범위

한 트랜잭션에는 한개의 애그리거트만 수정해야 한다.(트랜잭션의 범위는 최대한 작게)

- 한 트랜잭션에서 두 개 이상의 애그리거트를 수정하면 충돌이 발생할 가능성이 높기 때문에

 

애그리거트는 최대한 독립적이어야 하는데 다른 한 애그리거트의 기능에 의존하기 시작하면 애그리거트간 결합도가 높아지게 된다.

 

 

6. ID를 이용한 애그리거트 참조

애그리거트도 다른 애그리거트(루트 에그리거트)를 참조하는 경우가 있다. 

예시 1) Order는 주문한 회원을 참조하기 위해 회원 애그리거트(Member)를 참조 하는 경우

루트 애그리거트 참조

필드를 이용한 애그리거트 참조는 다음 3가지의 문제가 발생할 수 있습니다.

  • 편한 탐색 오용
    ->  애그리거트간의 결합도 증가
  • 성능에 대한 고민
    -> 지연로딩과 즉시로딩간의 성능 고민
  • 확장 어려움
    -> 서로 다른 DBMS를 사용하면 단일 기술로는 확장 어려움

이런 3가지의 문제를 완화할 때 사용할 수 있는 것이 ID를 이용해서 다른 애그리거트를 참조하는것

즉, 다른 애그리거트를 참조할 때 ID 참조를 사용한다는 점입니다. 

 

 

'DDD' 카테고리의 다른 글

[DDD] Repository의 조회 기능(JPA 중심)  (0) 2021.04.21
[DDD] Domain Driven Design 아키텍처  (0) 2021.04.11
[DDD] Domain Driven Design 개요  (0) 2021.04.06

블로그의 정보

꾸준히 공부하는 개발 노트

HeshAlgo

활동하기