Zpět na domů

DDD v ASP.NET: problémy škálovatelnosti po 30 agregátech

V projektech na ASP.NET s více než 30 agregáty DDD vede k poklesu výkonu kvůli těžkým dotazům EF Core, explozi specifikací a složitostem s transakcemi. Analýza problémů a doporučení k hybridním přístupům: CQRS, Vertical Slices.

Proč se DDD láme ve velkých projektech ASP.NET s 30+ rejstříky
Advertisement 728x90

Scalability of DDD in ASP.NET: Problems after 30 aggregates

Exceeding 30 aggregates in an ASP.NET project with Entity Framework Core causes critical issues due to the principle "one query – one aggregate." Each aggregate requires full loading with nested objects, generating SQL queries with numerous JOINs. This is especially noticeable in ERP systems or marketplaces where object hierarchies reach 5–6 levels.

Example of a typical query for the Order aggregate:

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();

The corresponding SQL contains dozens of LEFT JOINs, significantly slowing execution. Updating a single field in the order header requires loading all OrderItems as part of the aggregate, further increasing load.

Google AdInline article slot

Explosive growth of specifications and their limitations

The Specification pattern encapsulates retrieval logic, but with 30+ aggregates, the collection of Specification classes grows to hundreds of files. Composition via And/Or leads to suboptimal SQL due to dependency on EF Core's IQueryable.

Key problems:

  • Dependency on a specific ORM: specifications don’t work with Dapper or gRPC without rewriting.
  • Complexity in real-world scenarios involving roles, statuses, and time zones.
  • Time spent on abstractions exceeds actual business logic implementation.

Cross-aggregate transactions and infrastructure overhead

Scoped DbContext in ASP.NET Core suits Unit of Work in small projects. In large systems, business cases often require simultaneous changes across 5+ aggregates: creating an order, payment, warehouse reservation, and call center task.

Google AdInline article slot

Solutions contradict DDD:

  • Single database transaction – creates God services and violates aggregate boundaries.
  • Distributed Saga or Outbox patterns – increase complexity tenfold.

As a result, 70% of code becomes infrastructure "glue" for synchronization, not business logic.

Code navigation: thousands of files and slowed iterations

DDD structure (API/Application/Domain/Infrastructure + Features) with 30+ aggregates generates over 1,000 files. Changing a single field in an entity affects:

Google AdInline article slot
  • Domain entity.
  • Fluent API configuration.
  • Migration.
  • Command.
  • Validator (FluentValidation).
  • Handler.
  • DTO.
  • Mapper (AutoMapper).
  • Specification.
  • Tests.

This reduces development speed, complicates onboarding, and hinders maintenance.

What matters

  • Performance: heavy Include/ThenInclude generate inefficient SQL; API response times grow exponentially.
  • Specifications: do not scale, tied to EF Core, composition leads to errors.
  • Transactions: cross-aggregate operations force compromises between DDD principles and business needs.
  • Codebase: over 1,000 files slow iterations by 50–70%.
  • Hybrid approach: apply DDD only in the core, use CQRS/Vertical Slices for peripheral layers.

Recommendations for migration

For projects with 30+ aggregates, adopt hybrid architectures:

  • CQRS: separate commands and queries; use Read Models without Include.
  • Vertical Slices: group code by features, minimizing layers.
  • Feature-Sliced Design: achieve modularity without rigid layered architecture.

This preserves DDD principles in complex domains while simplifying the rest.

— Editorial Team

Advertisement 728x90

Číst dál