One of my previous posts was about my implementation of fetching strategies for NHibernate 2. This time, under the same title, I want to share with you some of my thoughts about NHiberante internal design.

Second version of NHibernate is based on the ‘event architecture’ (described here in section 11.2). Some of the most important NHiberate functionalities were rearchitected so that the main code is placed in a event listener. Thanks to this, NHibernate code base is now logically split into two parts: one that defines control flow (the ‘when’ and ‘what’) and one that does actual processing (‘how’). Users gain control over event registration so that every event handler in the system can be substituted by custom one. System is also not limited to one handler per event so one can register his own handler to pre- or postprocess data for the default handler. NHibernate, of course, comes with a set of default handlers which should be sufficient for 99% of cases.

Thanks to this event system, fetching strategy feature can be implemented in NHibernate 2 without modifying the codebase. It could be weaved in the save event processing replacing (in some cases) the default behaviour. My first approach was to write a custom event listener from scratch and register it to be used before the default listener. When special, fetching strategy-related, behaviour was to be used, my custom event handler was to be executed and the default one was to be skipped. Unfortunately, there is no option for prematurely exiting the event handling pipeline. Moreover, the default handler does not have a conditional statement for checking whether object was already fetched so my design forced fetching to be done two times. I had to think out a better approach.

Deriving from default handler seems to be ‘the way to go’ for substituting default fetching actions. I find this approach somewhat awkward since there is no possibility of combining more than one condition-based event handler. That is my first point: if only a premature exit (or to say it differently – cancelable events) capability would be added to the event system, one could seethe system as the Chain of Responsibility implementation.

Chain of Responsibility is in fact an event system (with somewhat static subscriptions usually) where individual chain links can play two roles (you can about roles in software design here): the Interceptor and the Handler. The Interceptor role is responsible for adding non-functional stuff to the system. It can be logging or security for example. The Handler role is responsible for the functional stuff – the main business goal of the system (like fetching data from database).

I see the main advantage of Chain of Responsibility in possibility of providing more than one Handler. NHibernate lacks this feature by default. Fortunately, it can be implemented by changing a little bit (using subclassing) the default handler to check, prior to executing the main code, whether instance was already fetched. If so, skip to the next link in the chain. The drawback of this approach is relying on client code (handler implementation) to properly handle skipping. There is also one more drawback which is inherent to CoR: there is no role separation of Handler and Interceptor. It can lead to interleaving functional and non-functional code in one class.

ASP.NET designers took a different way. They did their best to separate the roles. By doing so they sacrificed the flexibility of having more than one handler: the ASP.NET pipeline can consist of many IHttpModule’s – the Interceptors and precisely one IHttpHandler - the Handler. There is no easy workaround for that.

And one final though in the end: event driven architecture (such as the one of NHibernate or ASP.NET) can be an indication of both good and bad design. Events bring tremendous flexibility to the code: allow substitute almost anything. It is tempting to, when starting a project, bring events in only because of not knowing the actual requirements: I have events, I can plug everything everywhere. That is precisely what should be avoided when doing events. Use them to bring flexibility only where it is needed, not everywhere, not as a technical workaround for poor analysis. Usage of events should be derivative, not a substitute, of analysis. Both NHibernate and ASP.NET are in my opinion fine samples of the ‘good’ approach.

VN:F [1.9.13_1145]
Rating: 0.0/5 (0 votes cast)