Avoiding Repository pattern – implementing Onion Architecture with DbContext only

domain-driven-designentity-frameworkonion-architecturerepository-patternunit-of-work

I am trying to follow the Onion Architecture to design my application where I have the following layers –

  • Domain Layer: is the inner-most layer and defines repository interfaces
  • Infrastructure Layer: forms the outer-most layer peered with the Web/UI layer and implements the repository interfaces
  • Service Layer: is the middle layer where business logic resides and repository interfaces are injected
  • Web/UI Layer: forms the outer-most layer peered with the Infrastructure layer and handles DI configurations

Things are working. No issues so far.

I've come across several Stack Overflow answers and online articles that recommend not to implement Repository pattern when using Entity Framework and Entity Framework Core, because the DbContext itself is implemented to provide the functionality of the Unit of Work pattern while the each IDbSet<T> acts as a repository. I do understand the point and that’s very good. Because, less code to write. So, I thought let’s see if I can find a way to structure my application avoiding the repositories and using the DbContext only.

Some recommendations I've found suggests use of the DbContext directly in my Controllers, which I didn’t like. I don’t want my Controller code to get all messy with queries. I prefer them slim, and delegate the business processing and database operations to somebody else, like I currently do with the Service classes.

I've read this article Ditch the Repository Pattern Already and in the comment section in response to one comment the writer said he uses Onion Architecture and –

If an Application Layer exists as a discrete layer then it is injected
with the DbContext and possibly other dependencies. For a Web
application, a UI layer sets up all the DI, translates request to
calls to the Application layer, and maps responses onto view models to
be sent back to the browser. Other infrastructure layers like the
persistence layer I define the DbContext in sit at the outside layer
with the UI layer as "peer" layers.

Since according to Onion Architecture the dependency should only go inward what I really don't understand is how can you get a reference of DbContext (defined in Infrastructure Layer) in Application Layer through DI.

For those who prefer not to implement Repository and Unit of Work patterns with EF/EF Core could you suggest how can I do the same and still structure my application using Onion Architecture?

EDIT – Feb 03 2020

Thanks to Flater for his answer. I've gone through each and every link everyone provided. Thanks to all.

But it seems I failed to properly express my problem. I'll try once again. My application can be represented with the following diagram of Onion Architecture

Onion Architecture with no Application Layer

Each layer is implemented as a separate project for convenience. Since Onion Architecture dictates that dependencies go only inward –

Domain – has no dependency on any other project. Defines models and interfaces.

Service – has dependency only on Domain. Contains the Service classes which implements the Service interfaces. Service classes are injected with Repository interfaces.

Infrastructure – has dependency on Domain. It defines the DbContext. It contains the Repository classes which implements the Repository interfaces. Repository classes are injected with the DbContext.

Web/UI – has dependency on Service and Domain. It contains the Controller classes which are injected with Service interfaces and/or Repository interfaces. (This project also has a dependency on Infrastructure, but that is solely for DI configuration purpose).

Onion Architecture puts persistence operations at the outer most layer as part of Infrastructure and use Dependency Inversion to access them. This results in a loosely coupled design where the Application Core (comprised of Application + Services + Domain Layer) doesn't have any dependency on data access layer/technologies.

When I said –

what I really don't understand is how can you get a reference of
DbContext (defined in Infrastructure Layer) in Application Layer
through DI.

what I meant is, if I'm to discard the Repositories and use DbContext only, then I must use the DbContext from my Service classes, but DbContext is defined in Infrastructure and Service doesn't have a reference to Infrastructure.

To use the DbContext, the Service Layer it needs a direct reference to Infrastructure. Adding this reference violates the most fundamental concept required by Onion Architecture – dependencies should always go inward.
This requirement forces us to use DIP to resolve outward dependency and thus achieve the loose coupling.

So, if Service Layer have a reference to the Infrastructure, the Application Core is directly dependent on the Data Access Layer, and I don't think we can call it an Onion anymore.

That's where my question comes – how can I avoid the Repositories, use DbContext only but still adhere to Onion Architecture?

I'm just trying to implement something that I can use with smaller applications, because clearly Repo/UoW approach is overkill for some scenarios and not to mention more code to write.

Best Answer

The core argument for using repositories is to prevent leaking EF dependent code into your domain. That argument is not wrong, it just comes with a steep cost, i.e. a high-complexity uow/repo layer, which is now being regarded (by some, at least) as too high a price to pay for what it gives back.

By not using that uow/repo layer, you do actually let your domain depend on EF (the context and db sets). That much is unavoidable.

I once wrote a lengthy answer on repositories with EF. Summarizing my anti-repository conclusion: this is why it is called Entity Framework, not Entity Library. Frameworks are something you cannot reasonably abstract without effectively needing to duplicate the entire framework's structures (which negates the benefits of having said framework).

what I really don't understand is how can you get a reference of DbContext (defined in Infrastructure Layer) in Application Layer through DI

It is, in essence, no different from how you would get a reference to your self-built UOW if you had implemented an UOW/repo layer. You register your db context in your DI framework and then your service layer is able to have this context injected.

Here's a .Net Core example from one of my projects:

services.AddDbContext<MyDbContext>(options => options.UseSqlServer(connString));

It's possible to use an interface rather than the direct db context class, but I haven't really dealt with projects where that became a necessity. Use it where appropriate.

Note that this registration doesn't have to live in the top level project (as that would violate your onion layering). I actually perform this registration in my persistence project. Something along the lines of:

// In Persistence

public static class DependencyRegistration
{
    public static void Register(IServiceCollection services, string connString)
    {
        services.AddDbContext<MyDbContext>(options => options.UseSqlServer(connString));
    }
}

// In top-level Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    Persistence.DependencyRegistration.Register(services, "myConnString");
}

This way, your top-level project actually doesn't need to depend on the EF dll (since it doesn't refer to anything from EF), but it still has the authority to decide the connectionstring (from its own configuration) and still directs the persistence layer to set up the necessary EF-related DI registrations.

Note: for a pure onion architecture, the top layer project should not tell persistence to register its dependencies; but rather the top layer project tells the service/application layer to do their registration, and the service/application layer tells the persistence layer to do their registration.
My example isn't full onion but I kept it for simplicity's sake. Setting up the call chain layer-by-layer isn't complicated to set up but would needlessly lengthen my example.

Other infrastructure layers like the persistence layer I define the DbContext in sit at the outside layer with the UI layer as "peer" layers.

I don't really get what they mean here. This description of persistence and UI being "peer layers" sounds like it's at odds with what an actual onion architecture should be.


For a bigger example, check out the NorthwindTraders github repository. The author of this repo also has presentations on Youtube where he elaborates on the how (and why).

Related Topic