Thursday, November 18, 2010

The first Commandment: Thou shalt not reference the IoC container!

I recently received a very nice email asking about one of my ‘10 Advanced Windsor Tricks’ posts where I’d stated, without really backing it up:

“Without this trick you would have to reference the container itself, which is an IoC anti-pattern of epic proportions.”

So why is referencing the IoC container an anti-pattern?

The hint is the name 'Inversion of Control'. IoC a is core principle of OO design, the idea being that you don't depend on concrete implementations, but on abstractions (in our case, interfaces). We want to build our software like Lego, out of independent components, that supply to their environment all the information about what they need to work. That information is described in interfaces. When we have a service like this:

public class MyService : ISomeService
{
    public MyService(ISomeDependecy someDependency) {
    }
}

… it's telling us two things, firstly that it supplies a service described by the ISomeService interface, that is, anywhere where an ISomeService is required, MyService can be supplied. The second thing it tells us is that it requires ISomeDependency to work. It doesn't care how that's implemented, just that whatever is supplied obeys that contract.

The important point here is that MyService is described entirely in terms of it’s own domain. Inversion of Control is a principle not a technology. We don’t need a container at this stage.

Now if I'm referencing a container I've lost that information:

public class MyService: ISomeService
{
    public MyService() {
        var someDependency = StaticContainer.Resolve<ISomeDependency>();
    }
}

You might argue that the container is what it needs to in order to function, but we're really lying here, we could be using any dependency internally and it's not communicated to the outside world. Rather than describing our component in terms of its own domain, we’re polluting it with infrastructure concerns (the container).

By doing this you are making your life harder than it needs to be. Say I want to write a unit test for MyService, I now have to mock IContainer and get it to pass some mock of ISomeDependency, it's twice as much work. So not only have I lost information, I've made my own life as a developer harder.

But the most important message that this kind of code is carrying, is that the author has failed to grasp the real genius of IoC containers. I’ll try an example:

Say I have these components:

class Component1 : IComponent1
{
    Component1(IComponent2 component2) { ... }
}

class Component2 : IComponent2
{
    Component2(IComponent3 component3) { ... }
}

class Component3 : IComponent3

They are all registered with my IoC container. Now when I resolve IComponent1 like this:

var component1 = container.Resolve<IComponent1>();

the container will first create an instance of Component3, then an instance of Component2 passing it the Component3 instance in its constructor. Then finally it will create an instance of Component3 passing the Component2 instance to its constructor which it returns to the caller.

Deep inside our infrastructure, there is a single call to the container to get the root object of our application (our core application class if you will), all the cascading dependencies from that root object are discovered, constructed and supplied by the container. That's the magic of IoC containers, they glue our application together, but you never see them except for that initial, single resolve call.

So referencing the container itself is an anti-pattern of epic proportions, because it means you’ve failed to understand what an IoC container is. It's a bit like buying a tractor, hitching it up to a team of horses and attempting to plow a field with it.

18 comments:

  1. I think there are times when this is unavoidable. Such situations include times when only a portion of a code base is using an IoC container. Another would be when some of the object creation is out of control of 'your' code.

    ReplyDelete
  2. While I agree with what you're saying, I think it's wrong if your design patterns are immutable and cannot accommodate exceptions to the rule.

    I prefer not to phrase them as commandments rather recommendations.

    Sorry if that comes across as "wishy washy", but I've found that if you command people on how to develop, they're less likely to do it. Such is the developer breed (of which I'm no exception).

    ReplyDelete
  3. Anonymous6:14 pm

    Great post Mike, thanks. I've run into this anti-pattern too, and it generally uncovers a fundamental misunderstanding of the IoC concept. Usually my first clue is finding StructureMap being initialized in a unit test project. Love the tractor/horse analogy. :)

    ReplyDelete
  4. Anonymous7:40 pm

    Yes, well said. The circumstance where I have used this anti-pattern in the past is when I'm trying to transition an existing/legacy app to IOC. It's an intermediate stage until all dependencies can be resolved by the root container. But bring on the commandments I say - we can all sin a little, so long as we accept there will be some penance to be paid at a later date.

    ReplyDelete
  5. @Christopher, @James,

    Of course, like all blunt, uncompromising hard-and-fast rules, this one is meant to be broken :)

    I'll come clean. In the applications I write, there are usually several references to the container. These are almost always in the 'infrastructure', and are usually there to make up for the lack of DI in some host framework (yes, MVC I'm looking at you... much better in 3.0 though). One of my favourite patterns is Udi Dahan's Domain Events, but this requires a contain resolve call, but it's infrastructure, so I consider it OK. I could list a dozen other places where it makes sense.

    What _is_ incorrect IoC container usage is having hundreds of calls to resolve, or the container itself, scattered around domain services and controller code. The point of this post was to make anyone who dos that think again. I think it's worth making it a 'commandment' because that makes you think _really_ carefully before you break it.

    ReplyDelete
  6. @jbull, Oh yes, the container in unit tests anti-pattern. Very good point.

    Unless of course it's a unit test for container registration :)

    ReplyDelete
  7. @Dave, agreed, it's worth breaking just so that you can start doing DI when you're stuck with some untested legacy WebForms app.

    ReplyDelete
  8. @Mike H.

    I'm glad you clarified your post's intent.

    I felt it was sort of "no ioc container references, OR ELSE!" when I originally read it :)

    Keep up the interesting topic material!

    ReplyDelete
  9. hammett7:36 pm

    I think you're mistaken IoC for Dependency inversion in the 3rd paragraph.

    As for the explanation for the anti-pattern, I usually just say that composition containers are all about decoupling. If you intentionally couple your code to a container you're now doing what you're supposed to avoid.

    Besides, the dependency on the container itself is bad, but indirect dependencies on the container behavior is just as bad IMHO.

    ReplyDelete
  10. Hi Hammett,

    Is there a difference in meaning between 'dependency inversion' and 'inversion of control'? I always understood them to be talking about essentially the same thing.

    ReplyDelete
  11. Don't we talk about dependency injection and Inversion of control ?

    ReplyDelete
  12. Hi Mike,

    IoC and DI are two different things. What you are essensially talking about is the practice (or application) of DI. When talking about containers, you most often will refer to them as DI containers. The magic glueing is DI (Dependency Injection).

    IoC on the other hand, is a principle for loose coupling. It abstracts away the construction of a dependent object, thus "inverting" the control over the dependencies.

    You should've read my tweet ;) http://twitter.com/ilkerde/status/5280067030614016

    Best regards,
    Ilker

    ReplyDelete
  13. Hi Michael, Ilker,

    There are 3 terms under discussion here:

    Inversion of Control
    Dependency Inversion (Principle)
    Dependency Injection

    The last two are very often confused. Check out this Wikipedia article:
    http://en.wikipedia.org/wiki/Inversion_of_control

    "Inversion of Control is highly associated with dependency injection and the dependency inversion principle. Dependency injection is the main method to implement Inversion of Control."

    As I understand it 'Inversion of Control' and the 'Dependency Inversion Principle' are both different names for the same 'principle' of OO design. That you should decouple your software by depending on abstractions rather than implementations.

    'Dependency Injection' is a 'pattern' used to implement Inversion of Control/Dependency Inversion.

    I hope this clarifies things.

    I'm going to promote this comment to a blog post because it comes up so often.

    ReplyDelete
  14. Mike,

    I think it's very common to refer to containers as "IoC" containers, although they effectively apply DI (as in Dependency Injection) as a variation of IoC. For me at least, the name "IoC Container" is incorrect. It should be stated as "DI Container".

    However, if you think that this is the common understanding and usage, then I'm fine with all in this post. Nonetheless, I see IoC as being the principle and DI as a common practice.

    It's perfectly valid IoC to use a ServiceLocator, for instance. However, that's your critique anyway in this post, isn't it? In short, your message is "don't use SL, use IoC-Containers". This sounds weird for someone who thinks of SL and DI being (different) implementations of the IoC principle. That's all.

    We're not far from each other. In fact, we're on the same beach. The difference are the drinks and sunglasses ;)

    ReplyDelete
  15. Hi Ilker,

    OK, I understand you now, you're objecting the name 'IoC container'. I agree with you, DI container would be better, but IoC container seems to have become the preferred name unfortunately. I notice that Hammett prefers 'Composition Container', but that is even more vague :)

    Thanks for a great debate.

    ReplyDelete
  16. Could you give a real-world example of to illustrate what you mean by "a single call to the container to get the root object of our application"?

    ReplyDelete
  17. Hi Sean, have a look at TardisBank, if you look at the code there are only two calls to Resolve in the entire application. One in the controller factory and another in the domain event dispatcher. These are both infrastructure pieces. You won't find any reference to the container in controllers, model or service classes.

    https://github.com/mikehadlow/Suteki.TardisBank

    ReplyDelete

Note: only a member of this blog may post a comment.