Search code examples
c#entity-frameworkormdomain-driven-designddd-repositories

To use or not to use Repositories with an ORM


There are many voices saying that in this age of ORMs, there is no longer need for repositories. Recently I was thinking about it myself and their arguments made usually sense to me.

On my new project, using EF Core, I thought I try the no-repository approach to make things more straightforward and simple. Simplicity is always good, after all.

Things went pretty well, until I realized I often write things like this (this is not a real aggregate, just an artificial example):

ctx.Invoices.Include(x => x.User).ThenInclude(x => x.Address).Include(x => x.Items).FirstOrDefault(x => x.Id == id);

Alright, so every time I need to get an aggregate, I need to write this statement and there are chances, sometimes/someone will forget to include an important reference.

What about to put this code to a helper class?

class InvoiceHelper
{
  public Invoice FindById(int id) {...}
}

Isn't this just a start of a repository in disguise? So even with an ORM, I still find repositories kinda useful.

How do others do these things and still appear to be happy without repositories?

Some articles discussing no-repository approach:


Solution

  • If you believe in the rule of thumb "don't mock what you don't own", I fail to see how you could substitute your ORM functionality for a mock or stub in your tests without a Repository-like abstraction.

    Repository is a very important seam - adapter in Hexagonal parlance - between your application and storage.

    And yes, the fact that you needed to create a helper to factor in some data fetching behavior is a perfect illustration of that importance.

    [Edit] Thoughts on the "no repos" articles

    As often, most of the "repository backlash" is overreaction to misuse of Repository and UoW rooted in collective amnesia of the original reasons behind the patterns.

    • "Why hide a perfectly good framework ?" - EF is not! For instance, IDbSet mentioned by one of the articles is a walking leaky abstraction, from its very name to Attach() to all its extension methods. Also see below misuses and misunderstandings of methods like Update() provided by the "perfectly good framework".

      In contrast, the point of the original Repository pattern was to be a minimalist 3-method collection-like abstraction you can't go wrong with. Add(), Remove() and a few Get methods are all we need, not the bells and whistles of a whole framework.

    • "Repository doesn’t make testing any easier" : yes it does! The smaller the contract of a dependency, the easier it is to reason about, test, debug. A.k.a. KISS.

    • "1. Performance – sort/filter: Microsoft’s old (2013) implementation of the Rep/UoW’s has a method called GetStudents that returns IEnumerable. This means any filtering or sorting is going to be done in software, which is inefficient."

      Seriously? The lousiness of Microsoft tutorial sample code written years ago is the #1 drawback about a whole pattern? When you just have to create a method inside the repo implementation and it can be as optimized as you need!

    • A Rep/UoW would update an entity using the EF Core’ Update method - Says who? A straw man again. I've been using these patterns for 10+ years and never needed that method. SaveChanges() has always been the way to go.

    • The allure of the Rep/UoW comes from the view that you can write one, generic repository then you use that to build all your sub-repositories

      Absolutely not. This was not part of the original pattern and even largely identified as an anti-pattern in the community since the early days of DDD adoption.

    • "Option 2 - DDD-styled entity classes" aka domain classes that do data access... which destroys persistence ignorance. Not really a good idea IMO.

    I could go on and on about almost every argument there, but the gist of it is :

    If you're going to blame a pattern, do it in full understanding of its fundamentals, not because you don't like some broken implementation that's been copy pasted and cargo culted along generations of developers to the point of uselessness.