lundi 16 avril 2018

Repository, UnitOfWork along with service provider, what is he right way to inject all these dependencies into my controller?

I have an application that is written using c# on the top of asp.net MVC 5. I created a separate repository for each model in my app by implementing the following interface.

public interface IRepository<TModel>
        where TModel : class
{
    TModel Get(int id);
    IEnumerable<TModel> GetAll();
    IQueryable<TModel> Find(Expression<Func<TModel, bool>> predicate);
    TModel SingleOrDefault(Expression<Func<TModel, bool>> predicate);
    TModel Add(TModel entity);
    IEnumerable<TModel> AddRange(IEnumerable<TModel> entities);
    void Remove(TModel entity);
    void RemoveRange(IEnumerable<TModel> entities);
    void Update(TModel entity);
}

Although, I saw many implementation where each repository implement a Save method, i did not. I didn’t think it is a good practice where each repository save separately. Which is why I implemented a unit-of-work so that all of my repositories work as a cohesive unit. In my mind it should be the unit-of-work responsibility to handle the saving which enables transactions and the ability to rollback or commit changes when needed.

When I implemented the repository pattern, I felt it would be a good idea to extend each repository as needed where I can have custom IQueryable object returned by the repository to reuse query logic and simple call that from my controller or other places like view helpers. So I could do something like this in my repository directly

public IQuerable<Customer> GetCustomerByClientId(int clientId)
{
       // I have typically would have more logic here, but I stripped it out for simplicity.
       Return DbContext.Customers.Where(x => x.ClientId == clientId);
}

Then I came to realize that in some cases, I need to write some logic which requires other repositories to handle the business needs.

I came to realize that extending each repository logic with logic other than CRUD operation may not be a good idea.

Instead, it sounds like I should add a service provider for each of my repositories where the service provider providers a method to wrap the business logic/rules.

Using the above example, I would create a CuatomerServiceProvider which accepts an instance of the unit-of-work. Then the service provider will utilize the injected unit-of-work to handle the business need which will allow me to reuse my business logic and separate that out of my repositories. Thus, limiting the repositories to only CRUD operation along with a new method I called it Query() which enables me to create a query.

However, the issue with adding a service provider for each repositories means I have to inject a lot more classes into my controller’s constructor. So my controller constructor my need to look something like this

public HomeController(IUnitOfWork unitOfWork, Ilog log, ICustomerServiceProvider customer, ICarServiceProvider car, IUserServiceProvider user)
{
    _unitOfWork = unitOfWork;
    _log = log;
    _customer = customer;
    _car = car;
    _user = user;
    // ....
}

As you can see now I am stuffing all these dependencies into my constructor to be able to use them which sounds like a new problem I have to work with or solve. The good news that I am using an IoC container which resolves all these dependencies for me. But the fact that I have all these dependencies in the constructor is bothering me.

My questions are

  1. Is it really a good idea to take all of my custom logic out of the repositories and extract that into a separate service provider classes?
  2. Is injecting these services as needed one at a time is the way to go? Is there a better approach to make this process easy by creating a container for all of my services classes where I would inject one dependency instead of 3+?

Aucun commentaire:

Enregistrer un commentaire