IoC container solves a problem you might not have but it's a nice problem to have

On frame­works and libraries

A log­ging frame­work helps you log what's hap­pen­ing in your appli­ca­tion. A UI frame­work helps you ren­der and ani­mate UIs to the user. A com­mu­ni­ca­tion library helps con­nect­ing parts of a dis­trib­uted system.

All of these tasks and con­cepts are pretty easy to under­stand. They are quite down to earth, and there­fore, at a high level at least, easy to explain. They pro­duce tan­gi­ble, addi­tive dif­fer­ence in your application.

Also the code of your appli­ca­tion changes in order to use those frame­works and libraries. You add a log­ging frame­work, you write some code against its API and you end up with a file on your disk, or some text in your con­sole. You add a com­mu­ni­ca­tion library, inherit some base classes, imple­ment some inter­faces to des­ig­nate com­mu­ni­ca­tion end­points of your appli­ca­tion and you can see two processes exchang­ing infor­ma­tion. I hope you get the pic­ture and I don't need do go on describ­ing the tan­gi­ble, addi­tive and vis­i­ble results of using a UI framework.

What about IoC container?

So what about inver­sion of con­trol con­tain­ers? There's a lot of con­fu­sion around what they do, and why you should use one at all. Every now and then I meet a devel­oper, who says they read all the def­i­n­i­tions, intro­duc­tions and basic exam­ples, and they still don't get why would they use a con­tainer. Other times I'm hav­ing dis­cus­sions that can be sum­marised as (slightly exag­ger­ated for dra­matic effect):

I got one of the IoC con­tain­ers, put it in my appli­ca­tion, and then all hell broke loose. We got mem­ory leaks, things weren't appear­ing where they should, users were seing other users' data, we had to recy­cle the pool every hour because we were run­ning out of data­base con­nec­tions, and the app got very slow. Ah and also it destroyed main­tain­abil­ity of our code, because we never knew when some­thing is safe to refac­tor, since we never knew where and who would try to get them from the container.

Let's ignore the details for now and con­cen­trate on the wider sentiment.

So? Should I use it or not?

The sen­ti­ment is one of con­fu­sion, scep­ti­cism and frus­tra­tion. Was it a good idea to use the con­tainer? Should we have not used it? Would I use it if I was lead­ing the team?

Truth is, those aren't nec­es­sar­ily the right ques­tions to ask. You can't answer them in a vac­uum. Should I use my phone to twit­ter when I'm on the bus? While the answer might be "sure, why not" if you're a pas­sen­ger, it would be unequiv­o­cally "don't even think about reach­ing for your phone" if you're the driver.

I have seen appli­ca­tions where  intro­duc­ing a con­tainer imme­di­ately, would only worsen things. I also haven't worked with any .NET appli­ca­tion where, if archi­tected the way like to do things, the con­tainer wouldn't be ben­e­fi­cial in the long term.

It all boils down to the fact that con­tainer solves cer­tain prob­lems that arise if and when you build your appli­ca­tions a cer­tain way, and unless you do, you're not going to see the ben­e­fits it brings, sim­ply because the prob­lems con­tainer solves will not be the main prob­lems you'll be fac­ing, or will not appear in your appli­ca­tion at all.

What sort of archi­tec­ture are we talk­ing about?

Con­tainer has cer­tain require­ments in order to work smoothly.  First and fore­most it requires struc­ture and order. At a mechan­i­cal level, con­tainer takes over cer­tain level of con­trol over low level, mun­dane details of your  application's infra­struc­ture. It is how­ever still a tool, and unless the envi­ron­ment where it oper­ates is clean, with well defined lim­ited num­ber of abstrac­tions it will strug­gle, and so will you, try­ing to get it to work.

There's a lot of con­tent in the sen­tence above so let's dig a bit deeper.

The assump­tion con­tain­ers are mak­ing is, that you do have abstrac­tions. Get­ting a lit­tle bit ahead of our­selves, one of the main rea­son for using the con­tainer is to main­tain lose cou­pling in our code. The way we achieve this is by con­struct­ing our types in such a way that they do not depend directly on other con­crete classes. Regard­less if it's a high level type, or low level type, instead of depend­ing on one another directly, con­tainer expects they will depend on abstractions.

Another, related (pre)assumption is you will  build your classes to expose abstrac­tions. And not just in any way, but keep them small and crisp (struc­ture and order, remem­ber). To main­tain loose cou­pling, the abstrac­tions are expected to pro­vide just a sin­gle small task, and most classes will pro­vide just a sin­gle abstraction.

It is not uncom­mon for a con­crete class to pro­vide more than one abstrac­tion (for exam­ple to bridge two parts of the sys­tem) but in that case the con­tainer assumes you will sep­a­rate these two respon­si­bil­i­ties into two abstractions.

Large num­ber of abstrac­tions in your sys­tem will be reusable, that is a sin­gle abstrac­tion will be exposed by more than one type. In cases like that, the con­tainer assumes all the imple­men­ta­tions will obey the con­tract of the abstrac­tion, that is they will all behave in a way that will not be sur­pris­ing to any­one con­sum­ing the abstrac­tion with­out know­ing what con­crete imple­men­ta­tion is behind it.

The point above is espe­cially impor­tant to pro­vide class level exten­si­bil­ity. Cer­tain types in your sys­tem will depend on a col­lec­tion of depen­den­cies, and you should not have to go back and change them, when­ever you add a new type end­ing up in that col­lec­tion. The assump­tion is, the code using the abstrac­tions, as well as the abstrac­tions them­selves are built in such a way, that the type can be extended by swap­ping (or adding new) depen­den­cies it has, with­out the type itself hav­ing to change.

That's a lot of assump­tions, isn't it?

You may say the con­tainer makes quite a num­ber of assump­tions (or require­ments!) about how your appli­ca­tion is struc­tured. You wouldn't be wrong.

How­ever if you look up and read the pre­vi­ous sec­tion again, for­get­ting that we are talk­ing about con­tainer, does this feel like an archi­tec­ture you should be striv­ing for any­way? Does it feel like good, main­tain­able object ori­ented archi­tec­ture? If it does, then that's good, because that's intentional.

A con­tainer doesn't put any arti­fi­cial or arbi­trary cum­ber­some con­straints onto how you should build your appli­ca­tion. It doesn't ask you to com­pro­mise any good prac­tices aim­ing at max­imis­ing testa­bil­ity, main­tain­abil­ity and read­abil­ity of your code and archi­tec­ture. On the con­trary, it encour­ages and rewards you for that.

It's the inversion!

Finally we're at a point where we can talk about some answers. The con­tainer makes a lot of assump­tions about how your code is struc­tured for two reasons.

That makes the con­tainer dif­fer­ent from log­ging frame­work or com­mu­ni­ca­tion frame­work. None of them cares about how your code is struc­tured. As long as you imple­ment cor­rect inter­faces or inherit cor­rect base classes, and are able to call the right API they don't care.

Inver­sion of con­trol con­tain­ers are fun­da­men­tally dif­fer­ent. They do not require your code to imple­ment or inherit any­thing and there's no API to call (out­side of sin­gle place called com­po­si­tion root). Just by look­ing at code of your appli­ca­tion, you can't tell if there is a con­tainer involved or not.

That's the inver­sion of con­trol work­ing. This is what con­fuses so many peo­ple when they first start using con­tain­ers. The dif­fer­ence is so fun­da­men­tal from most other frame­works and libraries that it may be hard to com­pre­hend and make the switch.

Inver­sion of con­trol con­fuses peo­ple and makes it harder to see the value a con­tainer brings because the value is not tan­gi­ble and it is not additive.

Con­trast that with the exam­ples from the first sec­tion. With log­ging frame­work you can point a fin­ger at a piece of code and say "There, here's the log­ging frame­work doing its thing". You can point a fin­ger at your screen, entry in Win­dows event log, or a file on disk and say "There, here's the result of log­ging frame­work doing its thing".

Inver­sion of con­trol con­tainer is just "out there". You can't see the work that it's doing by inspect­ing your code. That ephemer­al­ness and intan­gi­bil­ity is what con­fuses many new users and makes it harder for them to see the value the con­tainer brings to the table.

To make things even slightly more con­fus­ing for new­com­ers, when you're using a con­tainer you don't end up writ­ing code to use the con­tainer (again, expect for a lit­tle bit of code to wire it up). You end up writ­ing less code as a result of using the con­tainer, not more, which again is harder to mea­sure and appre­ci­ate than code you can clearly see as a result of using other kinds of frameworks.

What isn't there…

The sec­ond rea­son why con­tainer makes all of these assump­tions about your code is very sim­ple. Unless your archi­tec­ture fol­lows these rules, you sim­ply will not have the prob­lems that the con­tain­ers are built to solve. And even if you do, they will be far from being your main concern.

Build­ing your appli­ca­tion accord­ing to the assump­tions that the con­tainer is built upon (I hope by now you fig­ured out that they are just SOLID prin­ci­ples of good object ori­ented C# code) your appli­ca­tion will be com­posed of types expos­ing cer­tain characteristics.

Fol­low­ing Sin­gle Respon­si­bil­ity Prin­ci­ple will lead you down the road of hav­ing plenty of small classes.

Open Close Prin­ci­ple will lead you down the way of encap­su­lat­ing the core logic of each type in it, and then del­e­gat­ing all other work to its depen­den­cies, caus­ing most of those small classes to have mul­ti­ple dependencies.

Inter­face Seg­re­ga­tion Prin­ci­ple will cause you to abstract most of those big num­ber of classes by expos­ing an inter­face or an abstract class, fur­ther mul­ti­ply­ing the num­ber of types in your application.

Liskov Sub­sti­tu­tion Prin­ci­ple will force you to clearly shape your abstrac­tions so that they can be used in a uni­form way.

Finally, if you fol­low Depen­dency Inver­sion Prin­ci­ple your types will not only expose abstrac­tions them­selves, but also depend on abstrac­tions rather than con­crete types.

Over­all you will have a big num­ber of small classes work­ing with a num­ber of abstrac­tions. Each of those classes will be con­cen­trated on its own job and largely ambiva­lent (and abstracted from) about who is using it, and details of who it is using.

This leaves you with a prob­lem of putting together a group of those small types (let's call them Com­po­nents) in order to sup­port a full, real life, end to end sce­nario. Not only will you have to put them together, but also, main­tain and man­age them through­out the life­time of your application.

Each com­po­nent will be slightly dif­fer­ent. For some of them you will want to have just a sin­gle instance, that is reused every­where, for oth­ers, you'll want to pro­vide a new instance each time one is needed, or may be reuse instances within the scope of a web request, or any other arbi­trary scope.

Some of them will have decom­mis­sion logic, like Dis­pose method. Some of them will have ambi­ent steps asso­ci­ated with their life­cy­cle, like "sub­scribe me to cer­tain events in event aggre­ga­tor when I'm cre­ated and them unsub­scribe me when I'm about to be destroyed".

Also thanks to the power of abstrac­tions and guar­an­tees put in place by fol­low­ing Liskov Sub­sti­tu­tion Prin­ci­ple you'll want to com­pose some of the com­po­nents using a fur­ther set of design pat­terns. You'll want to cre­ate Dec­o­ra­tors, Chains of Respon­si­bil­ity, Com­pos­ites etc.

Not to men­tion the fact that in addi­tion to depend­ing on abstrac­tions exposed by other com­po­nents (let's call them Ser­vices) your com­po­nents may also have some con­fig­u­ra­tion, or other "prim­i­tive" depen­den­cies (like con­nec­tion strings).

I hope you can see that the com­plex­ity of it all can quickly grow immensely. If you want to main­tain loose cou­pling between your com­po­nents, you will have to write a lot of code to put them together and main­tain all the afore­men­tioned qual­i­ties. The code to do that can quickly grow as well, both in size and complexity.

Con­tainer to save the day

Inver­sion of con­trol con­tain­ers exist to replace that code. I know it took me a while to get here, and I did it in a very round­about way, but there you have it — that's what IoC con­tain­ers do — they help you stay sane, and reduce fric­tion of devel­op­ment when adher­ing to good Object Ori­ented Design prin­ci­ples in lan­guage like C#.

I know it's not a sexy and con­cise def­i­n­i­tion, and some of you will prob­a­bly need to re-read this post for it to "click". Don't worry. It didn't click for me at first either. The value and goal of IoC con­tainer is abstract and intan­gi­ble, and the nomen­cla­ture doesn't help either.

The i-word

Some peo­ple feel that a lot of mis­con­cep­tions and mis­un­der­stand­ing about con­tain­ers comes from their very name — inver­sion of con­trol con­tainer. If you just hear some­one say those words, and leave it to your imag­i­na­tion, you'll most likely see… blank. Noth­ing. It's so abstract, that unless you know what it means, it's pretty much meaningless.

To alle­vi­ate that some peo­ple started refer­ring to con­tain­ers as Depen­dency Injec­tion con­tainer. At first this sounds like a bet­ter option. We pretty much under­stand what depen­dency in con­text of rela­tion between two objects is. Injec­tion is also some­thing that you can have a lot asso­ci­a­tions with, soft­ware related or oth­er­wise. Sounds sim­pler, more down-to-earth, doesn't it?

It does sound like a bet­ter option, but in my expe­ri­ence it ends being a med­i­cine that's worse than the dis­ease. While it doesn't leave you with blank stare, it projects a wrong image of what a con­tainer does. By using the word injec­tion it empha­sises the process of con­struct­ing the graph of depen­den­cies, ignor­ing every­thing else the con­tainer does, espe­cially the fact that it man­ages the whole life­time of the object, not just its con­struc­tion. As a result of that, in my expe­ri­ence, peo­ple who think about con­tain­ers in terms of DI end up mis­us­ing the con­tainer and fac­ing prob­lems that arise from that.

That's why, even though I'm quite aware Inver­sion of Con­trol Con­tainer is a poor name, that doesn't help much in under­stand­ing those tools, at least it doesn't end up caus­ing peo­ple to mis­un­der­stand them.

In clos­ing

So there you have it. I admire your per­sis­tence if you made it that far. I hope this post will help peo­ple under­sand what a con­tainer does, and why putting it in an appli­ca­tion that's not archi­tected for it, may end up caus­ing more prob­lems than benefits.

I hope it made it clearer what hav­ing a con­tainer in a well designed appli­ca­tion brings to the table. In fact I only spoke about the most basic and gen­eral usages sce­nario that every con­tainer sup­ports. Beyond that par­tic­u­lar con­tain­ers have addi­tional, some­times very pow­er­ful fea­tures that will add addi­tional ben­e­fit to your devel­op­ment process.

So even if you feel you don't have a need for a con­tainer right now, hav­ing the prob­lems that the con­tainer is meant to solve is often a sign of a good, object ori­ented archi­tec­ture, and those are good prob­lems to have.

  • Woj­ciech Turowicz

    Also helps with unit test­ing. See Machine.Specifications.Fakes.

  • http://leniel.net/ Leniel Macaferi

    Dear Krzysztof,

    I've never known some­one with so many con­so­nants in sequence in a name. :D

    I tried to com­ment using iPhone but DISQUS has a flaw that doesn't let one login because the but­ton is hid­den in the popup dia­log! So here I'm on the Mac mini.

    I really appre­ci­ate the effort you put in Wind­sor IoC imple­men­ta­tion. I'm a big fan and have been using it with­out a problem!

    One of the things that I like and that I always men­tion is that when using an IoC con­tainer you just write less code as you men­tioned in the post:

    You end up writ­ing less code as a result of using the
    con­tainer, not more, which again is harder to mea­sure and appre­ci­ate
    than code you can clearly see as a result of using other kinds of
    frameworks.

    One exam­ple of this is with Entity Frame­work: you don't have to write a lot of using state­ments every time you need to open a DB con­text. Using the .LifestylePerWebRequest() you can add a DB con­text in your base con­troller and have it injected auto­mat­i­cally by the con­tainer. It's like fire/configure and for­get. The con­tainer knows every­thing about han­dling the db con­text life cycle — the right time to instan­ti­ate and the right time to dis­pose it. Then all your inher­it­ing con­trollers just have to call Database.TableName.Single(...) any­where in your controller's code and you're good to go.

    If you care­fully con­fig­ure the con­tainer using the steps described in this excel­lent tuto­r­ial Wind­sor tuto­r­ial — ASP.NET MVC 3 appli­ca­tion (To be Seen) by Krzysztof Koźmic you will know how good it is to have an IoC con­tainer doing the heavy lift­ing for us. I do appre­ci­ate it… :)

    Keep the great work Koźmic!

    Best,

    Leniel

  • Ognyan Dim­itrov

    Dear Krzysztof,
    We use Wind­sor with Enti­tyFrame­work too for enter­prise web apps and it saves the day really.  First I thank you for your great work of imple­ment­ing the IoC and not last I thank you for all the doc­u­men­ta­tion and expla­na­tions with­out which I would not be able to see the ben­e­fits nei­ther the design prin­ci­ples of IoC. Now I love the SOLID prin­ci­ples and the design they bring and I refac­tor all around all of our appli­ca­tions in order to use them with IoC together. Thank you again for your hard work and for the shar­ing of the high qual­ity knowl­edge you have gained over the years.