DDD(Domain-Driven Design) 애그리게이트(Aggregate) 설계, 어떻게 해야 할까?
DDD(Domain-Driven Design)에서 애그리게이트(Aggregate)는 데이터 컨테이너가 아닌 일관성 경계(Consistency Boundary)를 의미함
과도한 객체 그래프 로딩(Massive Object Graph Loading)은 성능 저하 및 동시성 문제의 원인이 됨을 지적함
애그리게이트 분리(Aggregate Splitting)를 통해 성능 개선 및 유지보수성을 향상시키는 방법을 제시함
도메인 서비스(Domain Service) 및 애플리케이션 서비스(Application Service)를 활용하여 애그리게이트 간의 일관성을 유지하는 방법을 설명함
Fat Aggregate의 문제점과 성능 저하
본문에서는 Fat Aggregate의 문제점을 지적하며, 모든 쓰기 연산 시 전체 객체 그래프를 로드해야 하는 상황을 예시로 제시한다. 특히, ProjectRepository에서 `Include`를 사용하여 모든 관련 데이터를 로드하는 방식은 테이블 락(Table Locks), 성능 병목(Performance Bottlenecks), 그리고 동시성 문제(Contention)를 야기한다고 설명한다. 이러한 문제는 시스템의 확장성을 저해하고, 유지보수성을 악화시키는 주요 원인이 된다.
Lean Aggregate로의 리팩토링
애그리게이트를 분리하여 Lean Aggregate를 구성하는 방법을 제시한다. 핵심은 동일 트랜잭션(Transaction) 내에서 일관성을 유지해야 하는 데이터만 하나의 애그리게이트에 포함시키는 것이다. 예를 들어, 문서 첨부 시 프로젝트, 작업, 팀 멤버 정보를 모두 로드할 필요가 없으므로, Document와 Project를 별도의 애그리게이트로 분리한다. 이를 통해 각 쓰기 연산이 더 적은 테이블과 행에 영향을 미치도록 하여 성능을 개선한다.
도메인 서비스(Domain Service)와 애플리케이션 서비스(Application Service)의 역할 분담
애그리게이트 분리 후, 애그리게이트 간의 일관성 유지를 위한 전략을 제시한다. 도메인 서비스(Domain Service)는 cross-aggregate business rule을 표현하고, 애플리케이션 서비스(Application Service)는 리포지토리(Repository)를 로드하고, 트랜잭션을 관리하며, 변경 사항을 저장하는 역할을 담당한다. 이러한 역할 분담을 통해 각 모델의 크기를 작게 유지하고, 코드의 가독성을 높이며, 비즈니스 규칙을 명확하게 관리할 수 있다.
애그리게이트 분리 시 흔한 함정
애그리게이트 분리 시 흔히 발생하는 실수들을 지적한다. 특히, 도메인 서비스(Domain Service)에서 오케스트레이션(Orchestration) 로직을 처리하는 것과 테이블 구조에 따라 무분별하게 분리하는 것을 경계해야 한다고 강조한다. 또한, 단일 불변성(Invariant) 유지를 위해 여러 애그리게이트를 로드하거나, cross-aggregate 검사를 반복적으로 추가하는 경우 잘못된 분리일 가능성이 높다고 경고한다.