Software engineering blog of Clément Bouillier: Applying IoC/DI in application architecture

Thursday, April 9, 2009

Applying IoC/DI in application architecture

IoC and DI are two patterns more or less equivalent, I let Martin Fowler explains the details. IoC stands for Inversion of Control and DI for Dependancy Injection.
We will see in this post how to use effectively this pattern inside an application architecture, and which are the benefits of its use.

Application Architecture

There are a lot of architecture styles, but here we will focus on common approches : the layered architecture with generally 3 principal layers (that could be splitted in other layers themself, but we do not talk about it in this post...).
There are several approches :
  • in "classical" approach (Microsoft UI/BLL/DAL or Java Struts/Bean/DAO), we often talk about Presentation (or UI)/Business/Data
  • in DDD (Domain Driven Design), if I simply we have UI/Application Core/Infrastructure (I take the word of Jeffrey Pallermo in its "Onion architecture"
The "Onion architecture as described by Jeffrey Pallermo certainly leads to implement some good practices as IoC and DI, but it is also possible to do it in "classical" app
roach. I will come back on one project of my first project (starting at end of 2005) where I have to lead the application architecture (it was in .NET/C# but all in this post could apply to any OO language).

A little project story
When I started , I was aware of some good practices (thanks to DotNetGuru.org French community) like interfacing your classes, splitting in layers, using POCO, persistance ignorance, applying patterns (principally GoF patterns at this time)...but I wasn't aware of "Onion Architecture" approach then, so I try to implement a "classical" architecture (for the background, we do not use TDD).
This architecture was a little bit heavy to manipulate : for each new Web page, we need to create 3 interfaces and the 3 implementations, adding instanciation in some "home maid" Abstract Factory...and what we can notice it is that we do not realize why exactly we were coding this way, a little bit like robots in fact just because "it was the good practices".
And now I am working again on this project and nothing has changed, and it is great. Why ? Because I have learned some things I did not heard about 3 years ago, and I think it legitimates this architecture (and more over the Onion one in fact). It is what we will see next, but before have a quick overview of IoC/DI.

Overview of IoC/DI
I already give a reference to Martin Fowler post on the subject, but I would like to draw an overview of IoC/DI here for the purpose of the post. I will take an example with interfaces which represents the best exemple of IoC/DI use (even if you could imagine other use cases).
Typically, you have a class Client that relies on an interface IMyInterfaceA, which have an implementation MyImplA which one relies on IMyInterfaceB, which have an implementation MyImplB. So to link all these classes together, I have either:
  • to call MyImplA constructor in Client and MyImplB constructor in MyImplA, which create dependancy between all the implementations, loosing the benefits of interfacing (even if it is possible to enforce "localized" instanciation...but it needs to carefully control code which could be more )
  • to implement AbstractFactory pattern (I do not detail this point...)
  • or to use a IoC/DI framework
The IoC/DI framework will use the configuration file or configuration code to inject the dependancy of MyImplA when needing IMyInterfaceA and so on. Another thing to note is that it will inject recursively the dependancies, then when in Client class, I will request a IMyInterfaceA object, it will inject MyImplA as IMyInterfaceA, and MyImplB as IMyInterfaceB on which relies MyImplA class. I invite you to see details of the different injection mecanism : constructor injection, setter injection, method injection, interface injection...
So now, I think you should start to understand where I would like to go...

Applying IoC/DI
To resume, we have (or we need if starting) :
  • a layered application architecture using interfaces to separate layers (or even sub-layers)
  • one of the numerous IoC/DI frameworks (see here for .NET, Spring for Java...)
We are near done : we just have to use an IoC/DI framework in our application. We can certainly create a dependance on the framework everywhere we need it, but I would prefer creating a simple class that will be the only one that relies on the framework.
Then, for example (I do not assert that it is the best architecture), if we have consider the following layers :
  • UI
  • Business
    • Business Services which take care of business processes = 2 projects, one for interfaces, one for implementation
    • Business Entities which bring business rules = 2 projects, one for interfaces, one for implementation
  • Data Access Services which manage data access and manipulates Business Entities = 2 projects, one for interfaces, one for implementation
We can have the following hard dependancies :
We can add some dependancies, for example from BusinessEntityImpl to IBusinessService or from AnotherBusinessService to IBusinessService, but then a central point is that we have to take care of circular dependancies that could be made (for example if BusinessService depends on IAnotherBusinessService).
Nota that finally you no more need any new statement!

Ok ! But why using this finally ?
Yes, you are right if you are asking this question, but I have some answers beside the "ivory tour architect" answer : "because it is the right way" ;).
Since instanciation is delegated to the IoC/DI framework, you can then imagine a lot of things, but the one I prefer sounds like AOP (Aspect Oriented Programming). But it is not AOP with combined byte code, i.e byte code of generic cross-cutting concerns and your business byte code, it is done at runtime (yes then it could leads to performance issues but only in special cases...).
I give you an example in .NET with EntLib Policy Injection AB, it is really simple to add AOP if you already had IoC/DI :
  • In your Factory class that is the only one dependent on IoC/DI framework, rather than just calling the IoC/DI framework, call Wrap method of EntLib PolicyInjection class (David Hayden for more details)
  • Add in EntLib PolicyInjection AB configuration all the cross-cutting concerns you care applying only to a 'defined perimeter" (via policy, matching rules, see MSDN for more details).
Oh great ! How can I apply it to my project ?
If you are starting a new project, consider all the elements given in this post and be sure to understand what you are doing else it could lead to a disaster...
If you have an existing application, I see 2 cases :
  • either you are in a similar situation to the one I described (using interfaces, layers and other good practices...) and then it would be relatively easy to change your application step by step to reduce the technical debt (if considered like that).
  • either you are far from this situation, then consider a technical refactoring or if not possible, bear, cry or leave it depends ;)...but note that a technical refactoring could be done step by step also (but each step costs more than in the first case).
Hum...and what about performance ?
I think the performance problem should not be up front problematic to take care. Certainly, you should try to envision the performance issues you could encounter during developement to avoid starting again if the performance issue comes, but we should never say "I will never use this or that because it could lead to performance issues", it has to be studied in every cases.
Then I give here some advices to study performance impacts that could IoC/DI could have :
  • it is the "dynamic" instanciation (it does not necessarily use reflection since configuration could be done in code or configuration files could be loaded once at start) that costs more than a new, then consider instanciation strategies
  • consider using singleton pattern, it will avoid several instanciation when your class does support any state management
  • consider IoC/DI just for some of your objects, the relevant ones, but do not ban IoC/DI because one of your objects does not allow to use it
If I apply this in my last example, I could for example say that I would use IoC/DI for Business Services and Data Access Services classes with singleton strategy and do not use it for Business Entities (but then notice you will need dependancies to Business Entities implementation from Business and Data Access Services...)

1 comment:

Jacob said...

Great thoughts you got there, believe I may possibly try just some of it throughout my daily life.
software engineering services