IDbContext in the Domain: when teams make the trade-off, and when they should not

In Clean Architecture discussions, people rarely argue for dropping a concrete DbContext into Domain. The more common trade-off is subtler: define an IDbContext (or similar) in Domain/Core, inject that into application or domain services, and keep the EF implementation in Infrastructure.
The rationale is familiar: EF’s DbContext already is a unit of work and a set of repositories (DbSet<T>). Wrapping it again in IOrderRepository, ICustomerRepository, and IUnitOfWork can feel like ceremony, especially when the team knows they will not switch to Dapper, Mongo, or another ORM anytime soon.
So do people do this? Yes. Is it always wrong? No. Is it free? Also no.
What the trade-off actually is
You are not “putting EF in Domain” in the strongest sense. You are putting a persistence-shaped port in Domain:
IDbContextwithDbSet-like members or query/save methods- Domain/Application code depends on that interface
- Infrastructure provides
AppDbContext : DbContext, IDbContext
Compared with classic repositories, you avoid a pile of nearly empty wrappers. Compared with pure Domain, you still teach the core about tables/sets and SaveChanges as the primary persistence vocabulary.
Why teams accept it
The pragmatic arguments are real:
- DbContext already UoW + repositories: a second abstraction often only forwards
Add/SaveChangesAsync - You will not swap EF: the “replace the database” story is theoretical for many products
- Less glue: fewer interfaces, fewer mocks that mirror EF anyway
- LINQ stays expressive: complex reads do not get forced through awkward repository methods
- Faster delivery on CRUD-heavy or moderately complex systems where the Domain is not ultra-rich
For an internal line-of-business app with one database and a small team, that can be a conscious, honest architecture, not a mistake by default.
What you still give up
Even with IDbContext instead of concrete DbContext, Domain/Application starts thinking in persistence terms:
- Change tracking,
Include, and query shape leak into “business” code - Testing often means mocking a mini-EF or using an in-memory provider, not a pure domain model
- The interface tends to grow into “god port” (
DbSetfor every aggregate) - If you later need a second model (read store, event store, multi-DB), the shortcut fights you
- Rich domain behavior can get replaced by anemic entities + LINQ scattered everywhere
So the dependency arrow may still point inward, but the language of the core becomes the language of the database.
When the IDbContext-in-Domain trade-off is reasonable
- Single database, EF is a long-term given
- Mostly CRUD / workflow with limited deep invariants
- Small team that values fewer layers over textbook purity
- You still keep entities free of EF attributes where practical, and keep Infrastructure owning mappings/migrations
- You document it as a chosen compromise, not “Clean Architecture by the book”
When you should not do it
- Real DDD: aggregates, invariants, domain events, multiple models
- You need Domain tests without thinking about sets and change trackers
- Multiple persistence technologies or bounded contexts with different stores
- The “interface” would still reference
DbSet<T>/ EF types: that pulls the EF package into Domain and erases most of the benefit - You are only adding
IDbContextto claim Clean Architecture while writing infrastructure code in the core
A clearer alternative spectrum
- Strict: Domain ports like
IOrderRepository/IUnitOfWork(or use-case-specific gateways). EF stays in Infrastructure. Best for rich domains. - Pragmatic middle: Application layer depends on
IDbContextor a thin data port; Domain stays persistence-ignorant (entities + domain services only). Often the sweet spot. - Aggressive shortcut:
IDbContextvisible from Domain services. Acceptable only when Domain is thin and EF is permanent. - No theater: Skip Domain project for simple modules; use Application + Infrastructure with EF directly. Cleaner than fake purity.
The takeaway
Yes, teams do put an IDbContext-style abstraction toward the center because DbContext already behaves like repository + unit of work, and rewriting that with empty wrappers is often overkill when EF will not be replaced.
That trade-off is valid when you are honest about it. It is a problem when you pretend the Domain is persistence-ignorant while every use case speaks DbSet and SaveChanges. Prefer keeping Domain about business rules, and if you need the shortcut, put IDbContext at the Application boundary, or drop ceremonial layers entirely, instead of smuggling a database façade into the heart of the model.
We help teams pick boundaries that match the product, not the slide deck. If you are stuck between purist and pragmatic .NET architecture, we can help you draw the line once and stick to it.

