[DDD] 애그리거트 -3

2025. 4. 28. 16:18·📚 개발자의 서재/도메인 주도 개발 시작하기
해당 포스팅은 최범균 작가님의  도메인 주도 개발 시작하기 (P.114~120)를 읽고 정리한 글입니다.

 

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

한 객체가 다른 객체를 참조하는 것처럼 애그리거트도 다른 애그리거트를 참조한다. 애그리거트 관리 주체는 애그리거트 루트이므로 애그리거트에서 다른 애그리거트를 참조한다는 것은 다른 애그리거트의 루트를 참조한다는 것과 같다. 애그리거트 간의 참조는 필드를 통해 쉽게 구현할 수 있다. 이 방법은 개발자에게 구현의 편리함을 제공하지만 다음과 같은 문제를 야기할 수 있다.

 

편한 탐색 오용

한 애그리거트 내부에서 다른 애그리거트 객체에 접근할 수 있으면 다른 애그리거트의 상태를 쉽게 변경할 수 있게 된다. 한 애그리거트에서 다른 애그리거트의 상태를 변경하는 것은 애그리거트 간의 의존 결합도를 높여서 결과적으로 애그리거트의 변경을 어렵게 만든다.

 

성능에 대한 고민

애그리거트를 직접 참조하면 성능과 관련된 여러 가지 고민을 해야 한다. JPA를 사용하면 참조한 객체를 지연(Lazy) 로딩과 즉시(Eager) 로딩 두 가지 방식으로 로딩할 수 있다. 두 로딩 방식 중 무엇을 사용할지는 애그리거트의 어떤 기능을 사용하느냐에 따라 달라지고 다양한 경우의 수를 고려해 연관관계 매핑과 JPQL/Criteria 쿼리 로딩 전략을 결정해야 한다.

 

확장의 어려움

사용자가 몰리기 시작하면 트래픽이 증가하고 자연스럽게 부하를 분산시키기 위해 하위 도메인별로 시스템을 분리하기 시작한다. 이 과정에서 도메인마다 다른 DBMS를 사용할 수도 있고 다른 종류의 데이터 저장소를 사용하기도 한다. 이는 더 이상 다른 애그리거트 루트를 참조하기 위해 JPA와 같은 단일 기술을 사용할 수 없게 만든다.

 

이러 세 가지 문제를 완화하기 위해 ID를 이용해 다른 애그리거트를 참조한다.

 

ID 참조를 사용하면 한 애그리거트에 속한 객체들만 참조로 연결되고, 애그리거트 간에는 물리적인 연결이 제거된다. 이를 통해 애그리거트 경계가 명확해지고, 모델의 복잡도가 낮아지며, 애그리거트 간 의존이 줄어들어 응집도가 높아진다. 또한, 다른 애그리거트를 직접 참조하지 않기 때문에 지연 로딩이나 즉시 로딩 여부를 고민할 필요가 없어 구현 복잡도도 감소한다. 더불어 한 애그리거트가 다른 애그리거트의 상태를 수정하는 문제를 근원적으로 방지할 수 있어 각 애그리거트의 독립성이 강화된다. 마지막으로, 애그리거트별로 서로 다른 구현 기술을 적용하는 것도 가능해진다.


ID를 이용한 참조와 조회 성능

다른 애그리거트를 ID로 참조하면 참조하는 여러 애그리거트를 읽을 때 조회 속도가 문제 될 수 있다.

 

예를 들어 주문 목록을 보여주려면 상품 애그리거트와 회원 애그리거트를 함께 읽어야 하는데, 이를 처리할 때 다음과 같이 각 주문마다 상품과 회원 애그리거트를 읽어온다고 해보자. 한 DBMS에 데이터가 있다면 조인을 이용해서 한 번에 모든 데이터를 가져올 수 있음에도 불구하고 주문마다 상품 정보를 읽어오는 쿼리를 실행하게 된다.

Member memebr = memberRepository.findById(ordererId);
List<Order> orders = orderRepository.findByOrderer(ordererId);
List<OrderView> dtos = orders.stream()
	.map(order -> {
    	ProductId prodId = order.getOrderLines().get(0).getProductId();
        // 각 주문마다 첫 번째 주문 상품 정보 로딩을 위한 쿼리 실행
        Product product = productRepository.findById(prodId);
        return new OrderView(order, member, product);
    }).collect(toList());

위 코드는 주문 개수가 10개면 주문을 읽어오기 위한 1번의 쿼리와 주문별로 각 상품을 읽어오기 위한 10번의 쿼리를 실행한다. 즉 N+1 문제가 발생하게 된다.

 

이를 해결하기 위해 JOIN을 사용해야 한다. JOIN을 사용하는 가장 쉬운 방법은 ID 참조 방식을 객체 참조 방식으로 바꾸고 즉시 로딩을 사용하도록 매핑 설정을 바꾸는 것이지만 이는 원래 방식으로 되돌아가는 꼴이다. ID 참조 방식을 사용하면서 N+1 조회와 같은 문제가 발생하지 않도록 하려면 조회 전용 쿼리를 사용하면 된다.

 

애그리거트마다 서로 다른 저장소를 사용하면 한 번의 쿼리로 관련 애그리거트를 조회할 수 없다. 이때는 조회 성능을 높이기 위해 캐시를 적용하거나 조회 전용 저장소를 따로 구성한다. 이 방법은 코드가 복잡해지지만 시스템의 처리량을 높일 수 있다는 장점이 있다.


해당 내용을 정리하면서 어떤 방식이든 장단점이 존재한다는 것을 다시 한 번 실감했다. 결국 상황에 따라 최적의 솔루션을 선택하는 것이 중요하다는 것을 깨닫게 되었다.
저작자표시 비영리 변경금지

'📚 개발자의 서재 > 도메인 주도 개발 시작하기' 카테고리의 다른 글

[DDD] 리포지터리와 모델 구현 -1  (0) 2025.05.01
[DDD] 애그리거트 -4  (0) 2025.04.29
[DDD] 애그리거트 -2  (0) 2025.04.27
[DDD] 애그리거트 -1  (0) 2025.04.26
[DDD] 아키텍처 개요 -5  (0) 2025.04.25
'📚 개발자의 서재/도메인 주도 개발 시작하기' 카테고리의 다른 글
  • [DDD] 리포지터리와 모델 구현 -1
  • [DDD] 애그리거트 -4
  • [DDD] 애그리거트 -2
  • [DDD] 애그리거트 -1
l'avenirJun
l'avenirJun
  • l'avenirJun
    오늘도 꾸준히 개발
    l'avenirJun
  • 전체
    오늘
    어제
    • 분류 전체보기 N
      • 📚 개발자의 서재 N
        • 객체지향의 사실과 오해
        • Good Code, Bad Code
        • 도메인 주도 개발 시작하기 N
      • 🔧 트러블 슈팅
      • Java
      • Spring
      • 운영체제
        • 공룡책 학습
      • 알고리즘
      • GIT
      • 면접 지식
      • Spring 단기심화 2기
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    애그리거트
    good code bad code
    오블완
    specification
    매핑 구현
    책임
    코드트리
    리포지터리
    유스케이스
    캡슐화
    객체
    역할
    책임-주도 설계
    코딩테스트
    인터페이스
    추상화
    코드 계약
    협력
    객체지향의 사실과 오해
    애그리거트 루트
    코딩트리조별과제
    메시지
    표현 영역
    도메인 모델
    가독성
    티스토리챌린지
    모듈화
    타입
    도메인 주도 개발 시작하기
    DIP
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
l'avenirJun
[DDD] 애그리거트 -3
상단으로

티스토리툴바