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

Published: (December 23, 2025 at 04:01 PM EST)
2 min read
Source: Dev.to

Source: Dev.to

EF Core’s Role in Clean Architecture

  • What EF Core is

    • A persistence mechanism
    • A data‑mapping tool
    • An infrastructure concern
  • What EF Core is not

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

If EF Core leaks inward, Clean Architecture collapses.

Where EF Core Lives

Infrastructure
 └─ Persistence
     ├─ DbContext
     ├─ EntityConfigurations
     └─ Repositories

The Domain and Application layers must never reference:

  • DbContext
  • DbSet
  • EF Core attributes
  • EF Core LINQ extensions

The Hard Boundary: No DbContext Leakage

// ❌ DbContext leaking inward
class CreateOrderUseCase
{
    private readonly AppDbContext _db;
}

Consequences

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

Depend on Interfaces, Not EF Core

public interface IOrderRepository
{
    Task SaveAsync(Order order);
    Task GetByIdAsync(OrderId id);
}

Infrastructure Implementation

class EfOrderRepository : IOrderRepository
{
    private readonly AppDbContext _db;

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

EF Core stays isolated behind the repository abstraction.

Senior Guideline: Let the Domain Drive EF Core

  • 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.

Common Mistake: Leaking IQueryable Upward

// ❌ IQueryable leaking upward
IQueryable Orders { get; }

Why It’s Dangerous

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

Correct Approach

  • Keep LINQ inside repositories
  • Return domain objects or DTOs

Scenarios Where EF Core Is Not Ideal

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

Senior teams often combine:

  • EF Core for writes and aggregates
  • Dapper / raw SQL for reads

This hybrid approach is perfectly acceptable.

What EF Core Handles Well

  • Migrations
  • Change tracking
  • Transactions

The Unit of Work concept, however, belongs to the application layer:

public interface IUnitOfWork
{
    Task CommitAsync();
}

EF Core provides an implementation but does not define the abstraction.

Testing Guidelines

  • Do: Write integration tests with real providers; validate mappings and constraints.
  • Avoid: Mocking DbContext or overusing the InMemory provider for logic tests.

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

Warning Signs (Boundary Violations)

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

Each smell indicates a breach of the architectural boundary.

Summary

  • Correct usage: EF Core disappears behind abstractions, supports the domain, and remains replaceable.
  • Incorrect usage: EF Core becomes the system, dictates design, and 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.

Back to Blog

Related posts

Read more »