A week or so ago Gavin from the Hibernate team wrote a very humoristic post (you can read it http://blog.hibernate.org/cgi-bin/blosxom.cgi/2007/05/23#in-defence) where he made a clear case for ORM against OODBM. One of his points was that ORM is the only one of the two that can handle legacy in a satisfying way.
I have used ORM in several projects over the past years, and at least 2/3 of those has been against some kind of legacy database. The thing about legacy (or really screwed up design of the data model) is that they often don't conform to even the second rule of normalization. Mostly they are flat representations of the business problems at hand.
I though I should share one of my absolute nightmare stories and elaborate a bit on the design choices that where made to support the applications scenario over that nightmare.
This scenario is based on a database where the data model was built to support the the lowest common denominator of several RDMBS, where SQL Server, Oracle, DB2 and Sybase is just some of them. This means a data model with almost no normalization for some parts and extreme normalization for others. On top of that I'm certain that parts of that model has been written by an trainee or a monkey of some sort.
There is a table with date bookings, thees date bookings can be one of two things. Either it is a stand alone booking or it is a booking connected to a resource. If the booking is standalone, the ITEM_NO column will hold the value "Appointment" and the description column will hold a value. If it is connected to a resource, the ITEM_NO column will contain an ID of that resource and description will instead be null.
While ORM is really good at handling a lot of different mapping scenarios, some are just not mappable at all when it comes to object models and if it is really hard to find an object model to hold the data, the ORM will fall flat.
In this scenario it was really hard to create a model that actually made sense. Since the domain had no interest in tracking back to the resource but just wanted the resource name as a description if the description was empty. The idea was to only have a Appointment class with the information.
The first idea was to use inheritance and create different mappings for the two scenarios. Now that did not really work out. Inheritance in ORM uses two approaches, one is "table per class" which weren't suitable here since we only had one class the other is using a discriminator column to choose what concrete class to use. Not suitable as well. There was two possible ways of deciding how to separate them, either by the ITEM_NO column or the by the Description column and both of them had no clear distinct way to discriminate between concrete classes.
The second idea was to create a many-to-one relationship with the item class where we just got the value for the resource and use encapsulation in the Description property to make sure that we got the right information presented.
Something like:
protected virtual Resource Resource {...}
public virtual string Description { get { if ( Resource != null ) return Resource.Description; else return _description; } }
This is a perfectly valid solution. The problem I had with this was that the data model actually got to put constraints on my domain model and made it ugly, though functional. I did not want that.
So instead I let the Data Model get constraints from the Domain Model. The approach for this scenario that I took was to create a view in the database that supported a clean model. The view looked something like this:
create view vwAppointments as select no, dateBooked, case when resources.description is null then appointments.description else resources.description end as description from appointments left outer join resources on resources.item_no = appointments.item_no
Joy! Now I could model my scenario as it was supposed to be in the application and not care about the constraints the legacy database put upon me.
So, yes most ORM are good at mapping legacy, but helping them a bit by cleaning up the model with some nice views do help.
|