C
C#•3w ago
Edup

Repository Pattern and Clean Architecture

Whenever we perform a query, the ideal approach is to return only the necessary columns. However, whenever I see content about repositories, the SELECT is always done using the entity. In other words, the entire entity is selected from the database. In my projects, I create responses or DTOs. But DTOs are recommended in the application layer, while repository interfaces belong to the domain layer. Of course, I could map the entity to a DTO to protect sensitive data. However, I would still be loading all the data from the database, including many unnecessary fields. For example, in an online store: On the homepage, we only display promotional products with their price, title, and image. It doesn't make sense to fetch the entire product description from the database.
18 Replies
Angius
Angius•3w ago
You discovered one of the issues with repository pattern, yes One of the reasons it's generally not recommended to use this pattern with EF Because, to use it well... you would just re-implement what EF already gives you Which is pointless
Edup
EdupOP•3w ago
You have a point. But how do you abstract the data you're getting? Example... I have to get promotional products, I only need to show name and price (ProductSumamaryDto)... and I have 2 projects: Blazor / Api. Both of them need to get promotional products.
Angius
Angius•3w ago
The API queries the db and Blazor gets data from the API? Unless you mean Blazor Server Then you can use shared services And have a method that specifically gets you this DTO Or a CQRS handler Or an IQueryable extension method
Edup
EdupOP•3w ago
Well, I believe the best way would be a CQRS hadler on application layer, as you said. For while, I'm using selector expression. IEnumerable<T> GetDeals<T>(Expression<Func<Product, T>> selector);
Anton
Anton•3w ago
it should be possible to map multiple entities to the same table, it's just tricky but people never care and pull everything from the db I have tried to achieve it once, could not do it 2 entities same table is easy, 3 didn't seem to work I'm sure there is some workaround
D.Mentia
D.Mentia•3w ago
You don't abstract the data you're getting, another reason to skip repository pattern. Each endpoint needs something unique and specific from the DB, trying to share a query between them will usually result in one of the two getting more data than it needs. Even if both Blazor and Api currently get the same data, there's no guarantee they always will be in sync as each one's needs evolve in the future, and sharing any part of their queries (or even using the same DTO for both) is usually a bad idea If there is some guarantee and it's just identical code across them, then I'd focus on figuring out how to share that duplicated code as a whole, not just the query
Thalnos
Thalnos•3w ago
wouldn't that kind of violate the core idea behind an ORM, that each table maps to a class in your code, and each class in your code maps to a table? In case of inheritance it makes sense if you use Table Per Hierarchy approach then multiple inherited types would be in the same table, but besides that I think thats not a good idea, messes with the simplicity of an ORM. There seems to be a way to do this https://learn.microsoft.com/en-us/ef/core/modeling/table-splitting#table-splitting But then I would rather seperate into different DTOs, ViewModels or whatever the context than into different Domain Models and select only what's needed for the mapping tho 🤔
Anton
Anton•3w ago
The idea is to make read and update models, not necessarily write models To keep using ef core for this, but without having to overfetch The thing you linked is what I tried to use to implement this
Thalnos
Thalnos•3w ago
but you don't have to overfetch, you can just select or update what you want
Anton
Anton•3w ago
The example they give is for 2 entities mapping to the same table You do, because they won't be tracked otherwise Any projection is still a projection Even if it's to the same type it doesn't scale for 3 entities as easily "Track it after the fact" is what you're going to say
Thalnos
Thalnos•3w ago
but when I pick specific properties in the Select then EF will translate it to select prop1, prop2, prop3 opposed to select * will it not?
Anton
Anton•3w ago
might actually work, I'm not sure yes but it won't track the materialized entity because the select will count as a projection even if you say AsTracking, it's going to ignore it you're going to have to track them manually after the query, which I'm not sure is even going to work as expected
Thalnos
Thalnos•3w ago
well I'm doing a projection anyway then to some DTO or ViewModel or whatever, so even if it would be tracking the materialized entity it would be garbagecollected when the obj is out of scope anyway. So you gotta then map back to a domain model on your update, which seems fine
Anton
Anton•3w ago
I'm afraid it might end up trying to save nulls where it shouldn't at some point or something like that, it calls for subtle issues No need to materialize it if you're just going to map it later. The point of entities is that they're tracked by the system. Otherwise you can get away with projections for reads and ExecuteUpdate for updates. "So you gotta then map back to a domain model on your update, which seems fine" map what back? It's not "garbage collected anyway", it is extra memory you have to allocate that serves no purpose, it's not free
Thalnos
Thalnos•3w ago
it will be garbage collected when the program leaves the scope you were in
Anton
Anton•3w ago
I mean it will be garbage collected, but that is not free, you have to pay for it
Thalnos
Thalnos•3w ago
gc overhead is not somethign I care about ^^
Anton
Anton•3w ago
And it's not "when it leaves the scope", it is "at some unspecified time in the future, after it's left the scope"

Did you find this page helpful?