DEV Community

Cristian Sifuentes
Cristian Sifuentes

Posted on

C# Architecture Mastery — EF Core in Clean Architecture (Part 8)

C# Architecture Mastery — EF Core in Clean Architecture (Part 8)

C# Architecture Mastery — EF Core in Clean Architecture (Part 8)

Entity Framework Core is one of the most powerful tools in the .NET ecosystem.

It is also one of the most misused.

In Clean Architecture, EF Core is neither the hero nor the villain —

it is infrastructure, and infrastructure must be controlled.

In this Part 8, we’ll clarify where EF Core belongs, where it breaks architecture, and how senior teams use it without letting it dominate the system.


1. The Core Rule: EF Core Is a Detail

Your business rules must not depend on EF Core.

EF Core is:

  • A persistence mechanism
  • A data-mapping tool
  • An infrastructure concern

It is not:

  • A domain model
  • A business rule engine
  • An architectural foundation

If EF Core leaks inward, Clean Architecture collapses.


2. Where EF Core Belongs

In Clean Architecture, EF Core lives only in:

Infrastructure
 └─ Persistence
     ├─ DbContext
     ├─ EntityConfigurations
     └─ Repositories
Enter fullscreen mode Exit fullscreen mode

The Domain and Application layers must never reference:

  • DbContext
  • DbSet<T>
  • EF Core attributes
  • EF Core LINQ extensions

3. The DbContext Boundary

The DbContext is the hard boundary.

// ❌ DbContext leaking inward
class CreateOrderUseCase
{
    private readonly AppDbContext _db;
}
Enter fullscreen mode Exit fullscreen mode

This creates:

  • Tight coupling
  • Persistence-aware business logic
  • Un-testable use cases

Once DbContext crosses this boundary, architecture debt accelerates.


4. The Correct Abstraction: Repositories

The application layer depends on interfaces, not EF Core.

public interface IOrderRepository
{
    Task SaveAsync(Order order);
    Task<Order?> GetByIdAsync(OrderId id);
}
Enter fullscreen mode Exit fullscreen mode

Infrastructure provides the implementation:

class EfOrderRepository : IOrderRepository
{
    private readonly AppDbContext _db;

    public async Task SaveAsync(Order order)
    {
        _db.Orders.Add(order);
        await _db.SaveChangesAsync();
    }
}
Enter fullscreen mode Exit fullscreen mode

EF Core stays isolated.


5. EF Core and Domain Entities

Senior rule:

EF Core should adapt to your domain — not the other way around.

Avoid:

  • Anemic entities shaped for EF
  • Public setters everywhere
  • Persistence-driven invariants

Prefer:

  • Rich domain models
  • Encapsulation
  • Explicit invariants

EF Core can map to private fields and constructors.


6. LINQ Leakage: The Subtle Smell

This is a common mistake:

// ❌ IQueryable leaking upward
IQueryable<Order> Orders { get; }
Enter fullscreen mode Exit fullscreen mode

Why it’s dangerous:

  • Couples logic to EF query translation
  • Breaks abstraction
  • Pushes business rules into queries

Instead:

  • Keep LINQ inside repositories
  • Return domain objects or DTOs

7. When EF Core Is the Wrong Tool

EF Core is not ideal for:

  • Complex reporting queries
  • High-performance read models
  • Bulk operations
  • Heavy analytics

Senior teams often combine:

  • EF Core (writes, aggregates)
  • Dapper / raw SQL (reads)

This is not heresy.
It’s pragmatism.


8. Migrations, Transactions & Unit of Work

EF Core handles:

  • Migrations
  • Change tracking
  • Transactions

But the Unit of Work concept belongs to the application layer.

public interface IUnitOfWork
{
    Task CommitAsync();
}
Enter fullscreen mode Exit fullscreen mode

EF Core implements it — but does not define it.


9. Testing EF Core the Right Way

Do:

  • Integration tests with real providers
  • Validate mappings
  • Validate constraints

Avoid:

  • Mocking DbContext
  • Overusing InMemory provider for logic tests

If EF Core is hard to test, boundaries are likely wrong.


10. EF Core Smell Checklist

🚨 Warning signs:

  • DbContext injected into controllers
  • DbContext in domain services
  • EF attributes in domain entities
  • Business rules inside LINQ queries
  • Heavy reliance on lazy loading

Each smell is a boundary violation.


Final Thoughts

EF Core is a tool, not an architecture.

Used correctly:

  • It disappears behind abstractions
  • It supports the domain
  • It stays replaceable

Used incorrectly:

  • It becomes the system
  • It dictates design
  • It blocks evolution

In Clean Architecture, EF Core must serve the domain — never rule it.

✍️ Written by Cristian Sifuentes — helping teams tame EF Core so architecture stays clean, testable, and resilient.

Top comments (0)