홈으로 돌아가기

ASP.NET에서의 DDD: 30개 애그리게이트 이후 확장성 문제

30개 이상 애그리게이트를 가진 ASP.NET 프로젝트에서 DDD는 무거운 EF Core 쿼리, 명세 폭발, 트랜잭션 복잡성으로 인해 성능 저하를 초래합니다. 문제 분석과 하이브리드 접근법 추천: CQRS, Vertical Slices.

대형 ASP.NET 프로젝트에서 30+ 애그리게이트로 DDD가 무너지는 이유
Advertisement 728x90

ASP.NET에서 DDD 확장: 30개 이상의 집계를 넘어서는 도전

ASP.NET과 Entity Framework Core를 사용하는 프로젝트가 30개 이상의 집계를 초과하면, '한 쿼리당 한 집계'라는 원칙이 치명적인 성능 문제를 야기합니다. 각 집계는 중첩된 객체를 모두 로딩해야 하며, 이로 인해 수많은 JOIN을 포함한 SQL 쿼리가 생성됩니다. 특히 ERP 시스템이나 마켓플레이스와 같은 환경에서는 객체 그래프가 5~6단계까지 깊어져 문제가 더욱 심화됩니다.

일반적인 주문 집계 쿼리는 다음과 같습니다:

var orders = await context.Orders
    .Include(o => o.Customer)
        .ThenInclude(c => c.Address)
    .Include(o => o.Customer)
        .ThenInclude(c => c.Contacts)
    .Include(o => o.Items)
        .ThenInclude(i => i.Product)
            .ThenInclude(p => p.Category)
    .Include(o => o.Items)
        .ThenInclude(i => i.Discount)
    .ToListAsync();

결과적으로 생성되는 SQL은 수십 개의 LEFT JOIN을 포함하며, 실행 속도가 크게 저하됩니다. 주문 헤더의 단일 필드 수정만으로도 전체 OrderItems가 집계에 포함되어 로딩되며, 성능 부담이 지수적으로 증가합니다.

Google AdInline article slot

사양의 급속한 증가와 그 한계

사양(Specification) 패턴은 쿼리 논리를 캡슐화하지만, 30개 이상의 집계를 가진 프로젝트에서는 사양 클래스 파일이 수백 개로 늘어납니다. And/Or를 통한 조합은 EF Core의 IQueryable 의존성으로 인해 비효율적인 SQL을 생성합니다.

주요 과제:

  • ORM에 종속됨: Dapper나 gRPC와 함께 사용하려면 다시 작성해야 합니다.
  • 복잡한 조합: 역할, 상태, 시간대 등 실제 시나리오가 복잡해지면서 관리가 불가능해집니다.
  • 추상화의 부담: 추상화 설계에 소요되는 시간이 실제 비즈니스 로직 구현보다 더 큽니다.

집계 간 트랜잭션과 인프라 부담

ASP.NET Core의 스코프 기반 DbContext는 소규모 프로젝트에서는 단위 작업(Unit of Work)에 매우 효과적입니다. 하지만 대규모 시스템에서는 비즈니스 워크플로우가 5개 이상의 집계를 동시에 업데이트해야 하는 경우가 많습니다: 주문 생성, 결제 처리, 재고 예약, 콜센터 작업 생성 등.

Google AdInline article slot

사용 가능한 해결책은 DDD 원칙과 충돌합니다:

  • 단일 데이터베이스 트랜잭션 — 신의 서비스를 만들고 집계 경계를 위반합니다.
  • 분산 사가 또는 아웃박스 패턴 — 복잡성을 최대 10배까지 증가시킵니다.

결과적으로 코드베이스의 70%는 동기화를 위한 인프라 연결 코드이며, 비즈니스 로직이 아닙니다.

코드 탐색: 수천 개의 파일과 느린 반복 개발

DDD 구조(API/Application/Domain/Infrastructure + Features)는 30개 이상의 집계를 가질 경우 1,000개 이상의 파일을 생성합니다. 단일 엔티티 필드 변경은 다음 10가지 요소에 영향을 미칩니다:

Google AdInline article slot
  • 도메인 엔티티
  • 플루언트 API 구성
  • 마이그레이션
  • 명령어
  • 검증기 (FluentValidation)
  • 핸들러
  • DTO
  • 매핑기 (AutoMapper)
  • 사양
  • 테스트

이로 인해 개발 속도가 극도로 저하되고, 신입 직원의 정착이 어려워지고 장기 유지보수가 어렵습니다.

무엇이 가장 중요한가?

  • 성능: Include/ThenInclude 연산은 비효율적인 SQL을 생성하여 API 응답 시간이 지수적으로 증가합니다.
  • 사양: 확장되지 않으며 EF Core에 종속되며, 조합 시 오류 발생 가능성이 높습니다.
  • 트랜잭션: 집계 간 연산은 DDD 이상과 현실적 비즈니스 요구 사이의 타협을 강요합니다.
  • 코드베이스 크기: 1,000개 이상의 파일은 반복 개발 속도를 50~70% 감소시킵니다.
  • 하이브리드 접근법: 핵심부에는 DDD를 적용하고, 주변 기능은 CQRS 및 수직 슬라이스를 활용하세요.

마이그레이션을 위한 제안

30개 이상의 집계를 가진 프로젝트는 하이브리드 아키텍처를 채택하는 것이 좋습니다:

  • CQRS: 명령어와 쿼리를 분리하고, Include 없이 읽기 모델을 사용하세요.
  • 수직 슬라이스: 기능별로 코드를 그룹화하여 계층 구조의 복잡성을 줄이세요.
  • 기능 기반 디자인: 오니언처럼 엄격한 구조 없이도 모듈성을 달성할 수 있습니다.

이 접근 방식은 복잡한 도메인에서도 DDD 원칙을 유지하면서, 나머지 모든 부분을 단순화합니다.

— Editorial Team

Advertisement 728x90

다음 읽기