Complex Object Graphes
Recently I have been working on an application that require high level of configurability. I choose to use Castle's Windsor and Inversion of Control as the main principals when designing the application.
I think I took a slightly different route than the normal one. I use Windsor to generate the entire object graph once, and then I start working with it. Actually, it is a little more complicated than that. The object graph just need a push (a call to Start() ) and it start working on its own, with no additional input/output from outside.
When I am talking about object graph, I'm talking about roughly 30 to 50 objects at the moment, nested to about 4 - 5 logical levels at the moment (likely more in the future). The high level overview of this is:
- Coordinator Service - Start / Stop:
- Triggers (file arrived, time span expired, clock tick, etc):
- Actions (check db, call web service, handle file, etc):
- Helper classes
- Data Components
- Configuration Classes
- Nested Actions
- Helper Classes
- Work Managers
It is fairly easy to add something to the system, you decide what trigger the action, register a new trigger and the actions that follow it, and you are done. Most of the helper classes are already written, and Windsor just supplies them automatically. The biggest concern that I have in this issue is that I need to make sure that thread safety is maintained, which is actually pretty easy to do in this case. Mostly, the work manager that I posted about previously is taking care of that.
The entire code for the service itself looks something like this:
IWindsorContainer cotainer = new RhinoContainer(new XmlInterpreter("Windsor.config"));*
ICoordinatorService service = container.Resolve<ICoordinatorService>();
service.Start();
And the whole thing just start running itself.
Advantages of this approach:
- I isolated the functionality very clearly. Which means that I can truly focus on a single responsability per class, and pass additional responsabilities to other classes.
- Very easy to explain what each parts does.
- Easy to test in isolation.
- The value of the system is with the interactions between the objects, not in the code itself.
- Allows to extend the application very rapidly, because I don't need to touch any other code.
- Bringing a new programmer in requires teaching about IoC and Windsor. I spread the love.
Disadvantages:
- While fairly easy to grasp at a high level, it is hard to understand the nitty gritty details, because the picture is so big.
- Testing the system as a whole is complex, because so many things happen at the same time.
- Creating clear transaction boundaries are complex. Because I don't want to make a transaction dependency (where I implcitly assume that a class will always call another class), it require some thinking. I can't say that I solved this issue fully yet.
- Configuration bugs. Enough said.
- Bringing a new programmer in requires teaching about IoC and Windsor.
Despite the disadvantages, I like this method. Just to give some context, I made an initial deployment that went bad because the production server is 64 bits and didn't have the components that I relied on. I was able to replace at the client site, the dependent compoents with a custom one that I wrote in place, I plugged it in, and it just worked.
* RhinoContainer is an extention to WindsorContainer that gives some additional capabilties, mainly configuration objects and pre-registeration of some of the facilities. It is a really thin extention, less than 150 lines of code.
Comments
Comment preview