Wednesday 29 April 2009

A Fluent NHibernate update

Since James has announced the latest happenings in the ongoing saga of moving Fluent NHibernate to using a semantic model, I thought a companion post might be appropriate.

My first post on the Fluent NHibernate rewrite is as good a place to start as any. In it I explained the limitations of the existing implementation and outlined my strategy for dissolving those limitations. My branch differs from the trunk by separating the fluent interface from the mapping model. The fluent interface manipulates the underlying model, and a family of visitors build a Hbm object graph that represents that underlying model. That object graph is serialized to xml and then passed to NHibernate. This graph consists of classes that are generated from the NHibernate mapping schema, so the serialization step is all automatic and works flawlessly.

The use of the visitor pattern allows for two things. One, the mapping model is not tied to any particular output format. Sure we pass NHibernate xml today, but tomorrow it would be nice to pass it the Hbm object graph directly so it can skip the xml schema validation step. The day after it would be nice to pass it a prebuilt NHibernate.Mapping graph, which is an idea that Tuna Toksoz was exploring from within my branch. Secondly, the visitor implementation allowed for powerful manipulations of the mapping model that would otherwise be difficult. I talked more about the visitor implementation here.

Sure, passing NHibernate its mappings in a format other than xml would be nice some day, but there was another reason why I chose to use a Hbm graph instead of directly writing xml. Basically, its much harder to go off the rails when working with the Hbm objects. One example is that the NHibernate schema is finicky about element ordering and you simply don’t have to worry about this when working with Hbm objects.

As time went on the code base grew, things changed, but slow and steady progress was made. The problem is that slow and steady doesn’t always win the race – specifically in the case where the goalpost is moving as fast or faster than you! This is the classic rewrite problem. I’m sure you are familiar with it – its why all the well respected software professionals tell you that rewriting your app is a fools errand. And don’t get me wrong, I am in agreement here. I approached my work to rewrite Fluent NHibernate as a prototyping exercise - “one possible future” if you will. As the work continued I would discuss our long term goals with James and for a while we found ourselves hoping that we could replace the existing trunk with my branch, if only we could “catch up” to the trunk functionality. Of course this sort of thinking was dangerous, and the consequences are well described in James’s post. My intentions, while good, have lead to a lack of progress on the Fluent NHibernate trunk and this is simply not a healthy place to be.

James asked me whether I could see a way to merge the two code bases. Basically inject my model into the existing guts of the trunk. I told him I couldn’t see a way how, but I encouraged him to try. The problem, I’m sure you’ve already guessed, is that I was too “caught up” in my code and simply not prepared to make sacrifices. This is why James managed something that I could not – he merged my branch into the trunk code under an “integration”  branch, with all the existing trunk tests passing. Its not pretty, but I suspect that it represents the best direction to work from. We can now do what the experts tell us to do and refactor, rather than rewrite.

So what were the sacrifices? The main one is the Hbm object graph approach. Fluent NHibernate will only be capable of spitting out xml for the near future. Lets face it, its not a huge loss. Plus I say near future because the visitor implementation survived, so there is no reason why we couldn’t later start retrofitting my Hbm code in. I am yet to take full stock of the “casualties”, but I expect that everything that has been lost in the merge can be regained through a gradual process of refactoring and reintroducing code and ideas from my branch. It will be fun!

Finally I want to use this post as an opportunity to thank James for all his amazingly hard work on this great project. We don’t always see eye to eye but the thing that I often remind myself is that time and again he has proven to me that he has his finger on the pulse of our users and can see how this project can best fulfil their needs. Please keep it up James.

2 comments:

  1. Developers, by nature, often find it very difficult to admit even the most minor mistakes. We are always able to justify "imperfect" choices in design, implementation, or process. I give you strong kudos for the very objective way in which you reviewed your experiences with your Fluent NHibernate prototype/branch. I very much agree with your ideas related to using the hbm object graph. I think that would be a very worthwhile modification. While you are correct that rewrites are often "fools' errands", I would also mention that the longer one waits, the more difficult it is to correct deficiencies in an application. This is due, of course, to the increasing number of dependencies that often exist upon a section of code. If you feel there are issues, it is probably best to sound the warning bell now and loudly and correct those items before it becomes impractical. Keep up the great work!

    ReplyDelete
  2. Hi Anthony, thanks for stopping by! It was interesting for me to reread this post just now as FNH, as you may be aware, has just gone 1.0. I guess things turned out pretty well. The code base now has the necessary underlying structure; what little nastiness remains can slowly be refactored out. It was quite a learning process for me to see my rewrite successfully transplanted into the existing code base and I am sure FNH still has many more learning opportunities instore for me.

    ReplyDelete