IoC patterns – partitioning registration

I’ve blogged a bit in the past, more or less explic­itly, about pat­terns and antipat­terns of Inver­sion of Con­trol usage. This is yet another post that will (pos­si­bly) spawn a series. We’ll see about that. Note that this post is not talk­ing about any par­tic­u­lar IoC con­tainer and what I’m talk­ing about is generic and uni­ver­sally applic­a­ble to all of them.

His­tor­i­cally we started to reg­is­ter and con­fig­ure our com­po­nents in XML (exam­ple is in pseudo syn­tax, not spe­cific to any par­tic­u­lar framework).

<components>
   <register name="foo" type="Acme.Crm.Foo, Acme.Crm" as="Acme.Crm.IFoo, Acme.Crm" />
   <register name="bar" type="Acme.Crm.Bar, Acme.Crm" as="Acme.Crm.IBar, Acme.Crm">
	  <parameter name="bla" value="42" />
   </register>
   <!--... many, many more components like this...-->
</components>

Later we learned that it’s mas­sive pain in the neck to write and man­age, and we moved to code:

  
container.Register<Foo>().As<IFoo>().WithName("foo");
container.Register<Bar>().As<IBar>().WithName("bar")
   .WithParameter("bla", 42);
/* ...many, many more components like this... */

This had the advan­tage over XML of being strongly typed, refac­tor­ing friendly, but still shared the biggest draw­back of the pre­vi­ous approach. For each and every new com­po­nent you added to your appli­ca­tion you would have to go back and explic­itly reg­is­ter it.

To alle­vi­ate that, many con­tain­ers sup­port con­ven­tions – where nam­ing the type in the “right” way or putting it in the “right” place would be enough to make that type avail­able as a component.

  
container.RegisterAllTypesInNamespace("Acme.Crm")
   .AsInterfaceTheyImplement()
   .NamedAsLowercaseTypeName()
   .WithParameterFor<Bar>("bla", 42);

You would still need to do some fine­tun­ing with some of the com­po­nents, but for bulk of it, it would just work.

So where’s the problem?

While the last approach is evo­lu­tion­ally the most advanced it too comes with its own set of draw­backs, that can bite you if you don’t pay atten­tion to them.

If you par­ti­tion your appli­ca­tion abstract­ing imple­men­ta­tion detains with inter­faces and reg­is­ter­ing classes via con­ven­tions you will end up with highly decou­pled appli­ca­tion. So decou­pled in fact, that you will have no explicit ref­er­ence to your classes any­where out­side your tests. This can com­pli­cate things when a new per­son comes to the project and tries to fig­ure out how stuff works. Sim­ply fol­low­ing ReSharper’s “Go to definition”/”Go to usage” won’t cut it. Often you can infer from the inter­face or usage which class is used under which inter­face, but if you have generic abstrac­tion (like ICom­mand­Han­dler) and mul­ti­ple imple­men­ta­tions that get wired in a non-trivial way it may quickly become much more com­pli­cated to find out how the stuff gets wired and why it works the way it does.

Par­ti­tion your registration

Installers

To min­i­mize prob­lems like this its cru­cial that you par­ti­tion your reg­is­tra­tion cor­rectly. What do I mean by that?

First of all make it gran­u­lar. Most con­tain­ers have some means of divid­ing your reg­is­tra­tion code into pieces. Wind­sor has installers, Aut­o­fac and Nin­ject have mod­ules, Struc­tureMap has reg­istries… Take advan­tage of them. Don’t just cre­ate one and put all your reg­is­tra­tion in one. You will regret it when later on you try to change some­thing and you find your­self scrolling through this mon­strous class. Make it gran­u­lar. Par­ti­tion is apply­ing Sin­gle Respon­si­bil­ity Prin­ci­ple. Even if your installer (I will stick to Wind­sor lingo – replace with module/registry if you’re using any other con­tainer) ends up reg­is­ter­ing just a sin­gle type – that’s fine.

Remem­ber to name them cor­rectly so that it’s very quick and obvi­ous hav­ing a type, to find which installer is respon­si­ble for its registration/configuration.

To do that – also pay atten­tion to the name you give your installers. If the next devel­oper has to stop and think, or search sev­eral to find the right one for a par­tic­u­lar ser­vice you’ve failed.

Last but not least – keep them all in one vis­i­ble place. I gen­er­ally cre­ate folder/namespace ded­i­cated to hold­ing my installers, so that I can find them quickly and scan the entire list at once. It’s really very frus­trat­ing hav­ing to chase down installers all across the solution.

  • http://blog.zoolutions.se/ Mikael Hen­riks­son

    Good post, it needs to be told over and over again and the par­ti­tion­ing should not be restricted to just IoC con­fig­u­ra­tion but projects in gen­eral. DO par­ti­tion ALL your code :)

  • http://jcselke.blogspot.com/ Jan C Selke

    That's so right…
    The Sin­gle Repon­si­bil­ity Prin­ci­ple ist applic­a­ble almost everywhere.

    I'm look­ing for­ward read­ing the next part ;-)

  • Rob

    I think you make a good point, but could you give a con­crete exam­ple of how you divide your reg­istries? It strikes me that you could just end up with some­thing that is dif­fer­ent, rather than actu­ally bet­ter. I don't really see how divid­ing up your reg­istries, whilst con­tin­u­ing to use con­ven­tions, actu­ally helps the new devel­oper when they get to your code base. Are these two con­cepts not orthog­o­nal to one another?

  • http://kozmic.pl/Default.aspx Krzysztof Koźmic

    Rob,

    I kind of did give an exam­ple — look at the screenshot.

    This wasn't the point of the post thought, as other than the obvi­ous ones (Con­trollersIn­staller, ViewsIn­staller, Repos­i­to­riesIn­staller) they tend to be quite spe­cific to the application.

    The con­cepts aren't com­pletely orthog­o­nal. The way I see it — the more you rely on con­ven­tions the less is hap­pen­ing in your code explic­itly, the more impor­tant it becomes that your reg­is­tra­tion code is clean, easy to core­late with classes it con­fig­ures and well structured.

  • Rob

    Ah yes, that's the prob­lem with rss read­ers. Sorry.