So I had a discussion with a coworker on implementing contract from a Base class to an interface.
We have the following structure with DDD, Api -> Application -> Domain -> Infrastructure. In the infrastructure we use EF Core.
The following code example
Application
public interface IOrderRepository
{
IUnitOfWork UnitOfWork { get; }
Task AddOrderAsync(Order order);
}
Infrasctucture
public class OrderRepository : BaseRepository<Order, DbContext>, IOrderRepository
{
public OrderRepository(DbContext ctx) : base(ctx) { }
public async Task AddOrderAsync(Order order)
{
try
{
await AddAsync(order);
}
catch (Exception ex)
{
Log.Error($"Exception: {ex}");
throw ex;
}
}
/*
*
* Some other db methods
*
*/
}
public abstract class BaseRepository<T, U> where T : class where U : BaseDbContext, IUnitOfWork
{
protected readonly U _context;
public IUnitOfWork UnitOfWork
{
get
{
return _context;
}
}
public BaseRepository(U context)
{
_context = context;
}
protected virtual async Task AddAsync(T entity)
{
await _context.Set<T>().AddAsync(entity);
}
}
So I am arguing for, instead of implementing AddNAMEAsync methods in every repository to make AddAsync public virtual in the base class, and in corresponding interfaces and making use of the base class implementation. This way we also still have the possibility to orderride AddAsync if needed and also minimize unneccesary "duplicate code" in repositories.
My coworker on the other hand thinks that this will make the name too generic and when calling the repository you will not know which entity you are adding to the context by just reading the code. And aslo arguing on that we should not expose base class methods in interfaces, but that it instead should only be Parent -> Child exposure.
We are using SOLID principles and each handler only handles one entity/domain aggregate, we also have very clear naming on our variables/objects so you can easily see what you are adding to the context in the repository name as well as in the domain model
CodePudding user response:
- I think you are right about implementing the AddAsync() method in a base class and inherit other classes from it. Because, when you want to use
AddAsync()
you will first inject the related class and then use it. (It will provide reusable code-base for you)
public class MyClass
{
private readonly OrderService _orderService;
//..
await _orderService.AddAsync(...);
}
- There is no problem in terms of naming in this type of use, because the relevant service and what it will do are very clear.
Btw,
CreateAsync
orInsertAsync
could be a better naming in my opinion to demonstrate database operation.
CodePudding user response:
You can use AddAsync(T entity)
method in BaseRepository
, this will be provided reusable code for all your entities but this is already been implemented by Entity Framework Core. Do you really need to implement this structure again?
EF Core already provides the DbSet
as base repository and DbSet<Order>
as your order repository. Also provides DbContext
as a unit of work implementation.
You don't need to create your generic base repository, repository, and unit of work classes if you don't use any other ORM tool or approach of persistence with EF Core. EF Core already provides these patterns as encapsulated.
What will encapsulating the classes and methods provided by EF Core solve in this way? What are the differences between EF Core's DbSet and your BaseRepository?
See this sample project