본문으로 건너뛰기
홈
기술
기술 전체
프로그래밍68
컴퓨터 과학63
AI48
웹 개발36
인프라33
데이터31
소프트웨어 공학18
소개
← 목록으로소프트웨어 공학 › 설계 › 도메인 주도 설계

애그리거트

목차

애그리거트

  • ERD를 가지고 개별 테이블 간의 관계를 파악하고 구조를 이해하는데는 시간이 걸린다. 이런 복잡한 도메인을 이해하고 관리하기 쉬운 단위를 만들려면 상위 수준에서 모델을 조망할 수 있는 방법.

  • 관련된 객체를하나의 군(집합)으로 묶어 준다. 수 많은 객체를 애그리거트로 묶어 바라보면 상위 수준에서 도메인 모델 간의 관계를 파악할 수 있다.

애그리거트는 단순히 관련 테이블을 한 덩어리로 묶는 기준보다 한 번의 변경에서 함께 지켜야 할 업무 규칙의 경계에 가깝다. 주문과 주문 항목을 예로 들면, 항목을 추가할 때 총액과 항목 수가 어긋나지 않아야 한다는 규칙을 Order가 관리할 수 있다.

flowchart LR
  요청[주문 항목 추가 요청] --> 루트[Order: 애그리거트 루트]
  루트 --> 검사[업무 규칙 검사]
  검사 --> 항목[OrderLine 변경]
  항목 --> 저장[한 트랜잭션에서 저장]

외부 객체는 항목 컬렉션을 직접 고치지 않고 루트의 메서드로 변경을 요청한다. 루트가 내부 규칙을 확인할 수 있게 하기 위해서다. 다른 애그리거트와의 관계는 객체 그래프를 무한히 연결하기보다 식별자로 참조하는 방법을 검토한다.

경계가 너무 크면 간단한 변경도 많은 데이터를 함께 읽고 잠그게 된다. 반대로 한 규칙을 지켜야 하는 객체들을 지나치게 나누면 트랜잭션과 동기화가 어려워진다. 실제로 동시에 일관성이 필요한 규칙을 기준으로 경계를 조정한다.

주문 항목 추가를 예로 들면

주문에 A 상품 2개가 있고, 한 주문의 총 수량은 10개를 넘을 수 없다고 가정한다. A를 9개 추가하라는 요청은 현재 항목의 수량과 합쳐 11개가 되므로 거절해야 한다. 이 검사를 화면이나 저장소마다 따로 구현하면 경로별로 규칙이 달라질 수 있다. Order를 루트로 두고 항목 추가 요청이 항상 루트를 통과하게 하면 한곳에서 규칙을 검사할 수 있다.

import java.util.ArrayList;
import java.util.List;

final class Order {
    private static final int MAX_QUANTITY = 10;
    private final List<Integer> lineQuantities = new ArrayList<>();

    void addLine(int quantity) {
        if (quantity < 1) throw new IllegalArgumentException("수량은 양수여야 합니다");
        int current = lineQuantities.stream().mapToInt(Integer::intValue).sum();
        if (quantity > MAX_QUANTITY - current) {
            throw new IllegalStateException("한 주문의 총 수량은 10개까지입니다");
        }
        lineQuantities.add(quantity);
    }

    int totalQuantity() {
        return lineQuantities.stream().mapToInt(Integer::intValue).sum();
    }
}

처음 addLine(2)를 호출하면 총수량은 2다. 이어서 addLine(9)를 호출하면 9 > 10 - 2이므로 거절되고 목록은 그대로 남는다. 목록을 외부로 노출하지 않아 호출자가 검사를 우회할 수 없다. 실제 주문 항목에는 상품 식별자와 가격 등도 필요하지만, 여기서는 한 불변식의 위치만 보여 준다.

요청현재 총 수량결과
3개 추가2개허용: 총 5개
9개 추가2개거절: 총 11개

이 예제의 수량 제한은 설명을 위한 가정이다. 실제 애그리거트 경계는 업무 담당자와 확인한 규칙에서 출발해야 한다. 결제 승인이나 배송 준비처럼 주문과 다른 시점에 처리되는 작업은 별도 경계로 두고 이벤트나 명시적인 서비스 호출로 연결할 수 있다. 한 트랜잭션 안에서 즉시 지켜야 할 규칙과 시간이 지나 동기화해도 되는 규칙을 구분하는 것이 핵심이다.

메모리에서 검사만 한다고 동시 요청까지 안전해지는 것은 아니다. 두 요청이 각각 총수량 2를 읽고 5개씩 추가한 뒤 저장하면 제한을 넘길 수 있다. 저장소에서 버전 번호를 비교하는 낙관적 잠금이나 적절한 트랜잭션 정책으로 충돌을 감지해야 한다. 애그리거트 경계는 모델의 규칙과 저장 시 일관성 경계를 함께 설계하는 출발점이다. Martin Fowler의 애그리거트 설명도 외부 참조가 루트를 통과해야 한다는 점을 강조한다.

같은 카테고리의 글