So I've implemented the repository pattern in a lot of projects, but there seems to be a bit of a discussion on what is right with this pattern. Previously, I've always added Update or Create methods in side of a (generic) repository to modify the entities. This would always save changes to the context.
Recently I read a blog post about the repository pattern saying that is should act like a collection. It should have a Find(), GetList() and Add() methods, and varieties on those. But it shouldn't save the changes itself. And it should never have an Update() method because changes to the entities in the collection are references. We should let a Unit of Work save the changes.
Now I don't like the unit of work concept. Because my caller structure usually looks something like this for an average API.
Controller -> Service -> Repository -> DbContext.
Where my service would modify entities (business logic) and my repository would save changes. The underlying database provider can, and might, change. Below example would be a very basic method inside of my service.
public async Task Update(UpdateBookModel model)
{
var entity = await _repository.Find(model.Id);
// very simple just for the example
entity.Name = model.Name;
entity.ReleaseDate = model.ReleaseDate;
await _repository.Update(entity);
}
I'm starting a new project where I want to use the same structure, but I want my repository to act like a collection. Where the changes are saved somewhere else. This would normally happen in a UoW (according to the blog). But I want to take that away.
I'll have the exact same setup as above, but I'll remove the _repository.Update(). I'm using DI, where the DbContext is added scoped. So I thought I might be able to add middleware which will save any changes that happened in my request. Note: I will throw exceptions when something is not as expected, so changed won't be saved when that happens. I have other middleware to catch those exceptions and give the appropriate response.
I like how it make the code cleaner, and takes away responsibility for the service.
So why would this, on a architectural standpoint, be a good or bad decision? Or what is something that I should change?
I did not study any architecture on this matter, so I don't know al the fancy terms. But I am interested in this part of development.
Edit 1
So how would this work?
Because our DbContext is scoped, it will be the same DbContext throughout the whole request. This means that it's the same for every repository and also the same in the middleware.
I would just modify / add / remove data like I normally would in my services. I would add middleware that will act after the request is finished. And this will get the DbContext, which is DI injected, and just call SaveChangesAsync(). Which will push the local modified data to the database.
After thinking about it a bit more this would work for any API controller, because requests are short and data would be saved frequently.
But there is a problem when working with scopes that are long lasting, like Blazor (server) components. We would not save data here, until the components are disposed.
Because the services live in a shared project, which both an API and Blazor project access. It's a bad idea in those instances, because I would need to build an exception for Blazor projects. But I do still find this idea intriguing and I think there is more to it.
GetBooks(), but something use-case specific, likeGetBooksAwaitingCatalogization(), possibly with parameters; it's not supposed to be just a renaming of the DBContext API). These then call the same underlying database provider. 2/2