Search code examples
c#entity-frameworkentity-framework-coreclean-architecturedependency-inversion

Clean architecture: inverting dependency of EF DbContext


I stumbled upon this article that explains the principles of clean architecture. I want to apply those principles in ASP.NET Core + EF. I got stuck at this part:

The Application layer only depends on abstractions, defined in interfaces, and these interfaces are implemented in outer layers. For example, persistence concerns (such as saving an item to a database) are defined only in terms of requirements; the persistence logic in the Infrastructure layer implements these requirements.

Sounds perfectly reasonable, the Application layer should not know that EF is a thing, and only expose an interface which will then be implemented by the DB context in the Infrastructure layer.

However, when I try it, I encounter a blocker. Say my DB Context looks like this:

public class DbContext {
   public DbSet<Person> Persons {get; set;} = null!;
}

According to Clean Architecture, the DbContext should now implement IDbContext that resides in the application layer:

public interface IDbContext {
   public DbSet<Person> Persons {get; set;}
}

However, DbSet is a type defined in EF, so the application layer would need to depend on EF too, which, as per my understanding of the article and Clean Architecture, is against the principles.

The article also links a repository, and when I look at the dependencies of the Application layer, I find out that they actually made the Application layer depend on EF too.

Any ideas how to get out of this and achieve an Application layer independent of the infrastructure layer and EF?


Solution

  • There are different approaches (with different potential downsides):

    1. Use IQueryable interfaces instead of DbSet - though it will still be a leaky abstraction (for example methods like Include are EF-specific, so you will need to reference it when those will be needed)

    2. Do not expose context as part of your domain/application level at all V1:

      Use repositories (and/or Unit of Work) on top of EF Core. Note that this approach (or some versions of it) can be considered as an antipattern (for example)

    3. Do not expose context as part of your domain/application level at all V2:

      Use the CQRS pattern (or something similar) when on the domain/application level queries and commands will be declared as interfaces and will be implemented on the infrastructure level via EF.