views:

115

answers:

3

I have a InventoryController that gets a IInventoryRepository inyected, however my needs have changed, and now one of the controllers methods also needs to use another 2 repositories, ILoansRepository (to see the get info about loaned inventory items) and another one, where some stats and extra info are found.

The way it works is that a ViewModelBuilder class that gets called from an ActionMethod in the InventoryController, that is the one that actually needs those. Currently I was passing the IInventoryRepository from the controller to the builder, but how should I do it now? Should I get the 3 repositories injected into the controller, and then pass them to the builder, just as I've been doing now? Or should I just do a IoC.GetInstance()? (although I think that is an anti-pattern isnt it?)

thanks!

A: 

To much responsibility for controller.

Maybe you should create a special service to handle that and that service should use those repositories (through IoC) auto-wired by constructor.

dario-g
A: 
  1. If your controller does too much work, split it into several ones.

  2. If you inject 3 repositories just to create ViewModelBinder, don't: inject (I)ViewModelBinder instead. Let IoC container do its job and resolve the dependencies for you; moreover, this will simplify the architecture, the testing, etc.

  3. Using ServiceLocator / GetInstance isn't always avoidable; sometimes you don't have "root" injection point like MVC controller factory and can't control object creation - for example, of model binders. So, I let my model binders (not builders) call GetInstance but I make my own "root": for example, they call GetInstance<IModelResolver>, not GetInstance<Entity>.

queen3
Francisco Noriega
Its currently a separate repo since I need to have a history of all the loans for all products, current loans for all products, overdue ones, all loans for one product (its history), loans given to specific people, people with overdue loans, etc
Francisco Noriega
About separate repo, if you need 3 repos to create Builder, and 1 of them to get data, inject 1 repo and 1 Builder to the controller. Don't create objects from dependencies yourself.
queen3
As for builder/binder, that doesn't matter; the principles are the same. You don't "get the 3 repositories injected into the controller, and then pass them to the builder" - you inject the direct dependency that your client (controller) needs, and IoC container does everything for you. It has a lot of consequences, for example, you don't change 10 places where you created model builder if requirements change; you just change IoC config (actually it's automatically deduced). It's easier to test since you mock 1 class with required functionality, not 3 ones with optional methods... and so on.
queen3
+2  A: 

In situations like these, the following guidelines come into play:

  • Too many dependencies is a smell that you violate the Single Responsibility Principle.
  • Don't have more than four dependencies. This is a relative guideline. I personally strive to have less; I get restless as soon as I add a third dependency (see the first item above), but can live with up to four. More than that and I have to refactor.
  • Don't take dependencies just to pass them on.

As far as I can tell, with three dependencies, you are still more or less within the safety zone when it comes to the number of dependencies, although you should start watching that particular design aspect more carefully.

However, as I understand your current implementation, you simply pass on the dependencies to a ViewModelBuilder (thus violating the third bullet). A slightly better option would be to define an abstraction of that (say, IViewModelBuilder) and inject that into the controller instead of all three repositories.

Under no circumstance should you resort to the Service Locator anti-pattern (IoC.GetInstance()).

Mark Seemann
That sounds really good, although the Controller itself also needs the InventoryRepository to do CRUD, but perhaps passing the IRepo+the IViewModelBuilder... what do you think about that?
Francisco Noriega
What do you think about making an IViewModelBuilder<T>, and then have the Controller receive(IInventoryRepository repository, IViewModelBuilder<CatalogViewModel> viewModelBuilder)and have something like this in my IoC config: ForRequestedType<IViewModelBuilder<CatalogViewModel>>().TheDefaultIsConcreteType<CatalogViewModelBuilder>()and the catalog view model builder itself requires an instance of the InventoryRepo and the LoansRepo, but the IoC handles that toois that ok? it sort of feels a little bit too complicated!
Francisco Noriega
To me it sounds like a good refactoring. Not more complicated, just different, with clearer demarcations of responsibilities. The only thing still jarring here is that by mixing IInventoryRepository and the IViewModelBinder you have a 'low-level' service (the Repository) and a 'higher order' service mixed in the same constructor. It hints of an asymmetry, but it happens to me too all the time as well. However, when it does, I always keep an eye on this kind of asymmetry because it hints that the 'low-level' service might be replaced by another 'higher order' service as well.
Mark Seemann
:) well I think i'll keep that for now :) Thanks!
Francisco Noriega