C
C#2w ago
AlisterKB

Repository Pattern. EFCore. Single Responsibility (how much of it?)

https://paste.mod.gg/udwfheejnznx/0 here is the example of my 2 controller endpoints. there are 2 regions(#region IThinkISuccessfullyShovedToRepoitory AND #region IDonnoHowToShoveToRepos) in it 1 I think i have managed to cleanly convert, otherwise directly calling dbcontext in controller, to use repository. and 2nd region that I can't figure how much of what should go to each repo. So I understand that each repo should ideally be only using its own respective context (BlogRepo only call context.Blogs), but to what end? 100% is it a cardinal sin to include like and utlize context.Tags in BlogRepo?
BlazeBin - udwfheejnznx
A tool for sharing your source code with the world!
6 Replies
JakenVeina
JakenVeina2w ago
So I understand that each repo should ideally be only using its own respective context (BlogRepo only call context.Blogs), but to what end? 100% is it a cardinal sin to include like and utlize context.Tags in BlogRepo?
Absoutely not. When people say "the repository pattern is terrible" this is the kinda thing they're talking about. Enforcing arbitrary rules like "the blogs repository must only access the blogs table" that serve no functional purpose, and only tie your hands looking at this sample, your domain API domain looks roughly like.....
public class BlogPostDTO
{
public ImmutableArray<string> Domains { get; init; }
public ImmutableArray<string> Tags { get; init; }
...
}

public class BlogPutDTO
{
public long Id { get; init; }
public AuthorDTO Author { get; init; }
...
}

public class AuthorDTO
{
public long Id { get; init; }
...
}

public class BlogsController
{
public async Task<ActionResult> AddBlog(BlogPostDTO dto);

public async Task<ActionResult> EditBlog(BlogPutDTO dto);
}
public class BlogPostDTO
{
public ImmutableArray<string> Domains { get; init; }
public ImmutableArray<string> Tags { get; init; }
...
}

public class BlogPutDTO
{
public long Id { get; init; }
public AuthorDTO Author { get; init; }
...
}

public class AuthorDTO
{
public long Id { get; init; }
...
}

public class BlogsController
{
public async Task<ActionResult> AddBlog(BlogPostDTO dto);

public async Task<ActionResult> EditBlog(BlogPutDTO dto);
}
for this, I would say your repository layer should look like.....
public class BlogsRepository
{
public async Task<BlogViewModel> InsertAsync(BlogCreationModel model);

public async Task<BlogViewModel?> TryUpdateAsync(BlogMutationModel model);
}
public class BlogsRepository
{
public async Task<BlogViewModel> InsertAsync(BlogCreationModel model);

public async Task<BlogViewModel?> TryUpdateAsync(BlogMutationModel model);
}
Looking at your sample code, the majority of the logic in those controllers would move down into the repository methods not all of it, though all the input validation sanitization can stay, if you're going to do it server-side The author check can maybe move to an AuthorsRepository
public class AuthorsRepository
{
public async Task<bool> ExistsAsync(long authorId);
}
public class AuthorsRepository
{
public async Task<bool> ExistsAsync(long authorId);
}
you can also just ditch BlogCreationModel and BlogMutationModel and just split those into individual parameters, if you want if it were me, I would keep them because they'd also serve as the input DTOs for the controller actions cause all that input sanitization would be done in the client but bottom line, if you're going to go with a "repository" pattern, the goal should be to isolate your logic for doing data access and data persistence NOT to isolate different types of data from one another and ONLY if you actually have a real reason to want business logic and data access logic separated, I.E. testing if you're not going to actually LEVERAGE the fact that you've split this across two layers, don't
AlisterKB
AlisterKBOP2w ago
I cannot express how much i appreciate this! I was dying wanting to reach for other entities (especially in the simplest project of a blog -for learning concepts- ) and at times felt as if I'm just writing wrapper for efcore linq methods! Thank you so much 🙏 Also as you mentioned, reaching for and trying repository pattern was to be able to be playing with and tackling testing. thank you again!
JakenVeina
JakenVeina2w ago
and you might find that having a repository layer is just more ceremony than it's worth perfectly fine conclusion me, I just find doing testing with EF Core a MASSIVE pain in the ass mocking out the repository layer, so I can test JUST business rules and logic is really helpful and for testing the data access layer, it's helpful in reducing the footprint of how much database you have to fake/mock depending how you split things up
AlisterKB
AlisterKBOP2w ago
understood 🫡 , I got more than I asked for (in the best possible way) I will definitely keep your advice handy moving to test. still undecided if I wanna (or should) settle for test DB or mocking, but imagine each have their own strength, drawbacks and place, and perhaps since the goal is learning, would branch off and try both.
JakenVeina
JakenVeina2w ago
absolutely
AlisterKB
AlisterKBOP2w ago
ill be marking this post as resolved not to clutter the server, and thank you again one last time.

Did you find this page helpful?