Complex Object Graphes

time to read 6 min | 1130 words

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.