Okay – so recently I’ve started presenting a session to various groups involving the well known IOC container “StructureMap” – and despite being pretty clear about the contents of said talk I’m getting quite a bit of backlash for demonstrating anything that even remotely resembles service location.
As we all know, service location is bad – but why do we know that? Because we’ve been told it’s bad? And why were we told it was bad? Let’s very briefly dig into this and see where we end up:
Why is service location evil?
Consider the following WebForms style code:
This is the kind of un-testable code that we started moving towards using Dependency Injection to solve in languages that have the notion of strongly defined types and constructors and the means for doing such things.
We can immediately remove a coupling problem here and increase testability by injecting those dependencies via the constructor like so:
Okay, this isn’t necessarily the best example in the world (Simply moving our dependencies into the constructor to “improve testability and decrease coupling” isn’t the silver bullet everybody seems to think it is), but it serves to show the next snippet of code which our IOC containers are built to help us with:
With typical applications built on top of frameworks such as say, ASP.NET MVC – this dependency graph can become quite large in most typical systems built out of various service layers, repository layers, etc. IOC containers popped into existence to make the above code more manageable like so:
The aim from all this would be simply to be able to have a single call to container.GetInstance which would compose the entire application for us and we could carry on with just writing code and tests.
What we have here, are classes which up front specify their dependencies via their constructor arguments, which makes creating SUTs in our tests easier, and reduces ambiguity in our code base.
In certain circumstances due to bad design (For example, ASP.NET Webforms where we don’t actually have control over the creation of System.Web.Page), it becomes almost necessary to write code that looks something like this:
In this example, we are making several calls out to the container to resolve some dependencies for our Page class – the problems with this are immediately obvious. These are hidden dependencies, we can’t know from looking at the class from the outside what it needs from the container, and testing will therefore get sticky and our system will be harder to maintain.
This, is service location, and this is evil – I’m not disagreeing with this and I think we’d be hard pushed to find anybody who thinks the above code is a good idea. When people say “Service location is evil”, they are thinking of the above example.
Here is another expression that tends to be shouted out quite loudly – “You should only be calling your container in one place in your application” – this expression has come because if you are using your IOC container for DI you should be able to construct your entire application from the “root” with a single request.
Moving beyond using your IOC for DI
Now here is the thing – modern IOC containers like StructureMap are moving beyond simply providing the facility to inject dependencies into our classes – they’re venturing into discovery territory, having the ability to automatically wire up concrete implementations to interfaces via convention is just the start of things – they can also be used to find all the implementations of say “IPlugin” and do something with them – this is an extension of their original purpose.
When I start using my IOC container to compose these plug-in systems, I do so because I want those plug-ins and their dependencies to also have the advantage of having their dependencies resolved and injected via their constructors without those implementers knowing how that works.
I could use MEF for some of this, but at this point I am already using a tool that will not only do a good job of discovery, but will also resolve all the dependencies set up in our boot-strapper - MEF as it currently stands does not make a good IOC container and its primary job is discovery which last time I checked was still worked via a WTF mess of attribute soup (I massively prefer those almighty conventions).
I therefore come to the following snippet of code taken from the RavenGallery project:
(Ignoring that this command handler is also in charge of the Unit of Work), the takeaway point is that I’m passing in the container and using it to find a command handler for the command that has been passed in.
Standard stuff really, so if I pass in a comand where T is RegisterNewUserCommand ala
Then the following type will be resolved
And all because in my StructureMap configuration I called
Hold on, that’s service location!! You’re calling into the container directly!!
Yes, and you know what – I still sleep well at night.
Going back to our example of why service location is bad, this example has nothing to do with that. When using your IOC container for nothing beyond plain old DI then sure – 99.999% of the time you’re doing it wrong if you end up with service location – but when you move into the territory of using the IOC container for discovery and run-time resolution there is little to no benefit to trying to work around a call into the container which – by the way – does a really good job of it already.
At this point we’re in the glue of our application, our infrastructure and we’re using the container as another piece of infrastructure – not as a workaround to avoid injecting our dependencies explicitly.
This is elegant and just works, I don’t think the “rule” applies in this kind of situation and I’m not the only one who seems to thinks so.
There is a difference in my mind therefore, between service location and dynamic dependency resolution. (Feel free to give me some proper words to put in here folks, my terminology-fu is weak)
A side note – ASP.NET MVC3
There are some complaints that ASP.NET MVC uses the container as a service locator – primarily because a single web request results in quite a few calls to the container from all over the place. This occurs because support for it has just been bolted on as an afterthought to appease the masses who complained that various aspects of ASP.NET MVC were untestable.
It’s not quite the same thing as this, there are a number of places in the framework where had the framework been built around the notion of composition in the first place we wouldn’t need all those calls – but really, I can’t say I have too huge a problem with this either, it’s not an awful compromise around a framework that has already gone down an awkward path and has needed to rectify it in a simple and backwards compatible a manner as possible.
Another side note
I’m not entirely sure that creating our entire application hierarchy with an IOC container is actually a Good Thing, and I’m moving away from it in my more recent explorations – but that’s a discussion best left to the GOOS group and for another time.
2018 © Rob Ashton. ALL Rights Reserved.