Thursday, August 23, 2007

As we've used NHibernate more and more we've begun to hit against issues, particularly when working against a "legacy" database. So if you are working against a green fields database then your probably fine, you can design from the domain down but if you're not then I think two issues in particular are worth bearing in mind:

Inability to map inheritance at the component level.

Inability to map unidirectional many-one in a satisfactory way in some cases.

Before I continue I should say two things. The first is that the class names are unimportant, I made them up as I don't want to use real class names or examples from our project. Secondly I am not going to provide solutions, I don't know if it’s even possible for an ORM solution to handle these issues better than NHibernate does but you should still know about them.

Take the example of an Customer table that has 50+ columns, don't ask me why but it does. You want to redesign the table but thats something to be planned and executed carefully, DB refactoring is painful especially when you have multiple legacy systems and reports/DTS's working against the database.

So for now you want to design your domain model to have a core Customer class and then lots of little inheritance hierarchies off it. For example if the Customer came directly from your Web site then maybe 5 fields in the database are used and if they came indirectly its a different 10.

In each of the two cases you have different behavior and certainly different validation requirements so you decide to have an ICustomerSource interface (making this up as I go) with two subclasses called DirectCustomerSource and ExternalCustomerSource. The Customer will then have a reference to an ICustomerSource which it will be passed in it constructor.

The problem is you cannot do this because your mapping would have each subclass of ICustomerSource mapped as a component, meaning that when the Customer is saved so should the ICustomerSource. The problem is that the component mapping does not support mapping inheritance so, as far as I know, you are slightly stuffed.

You could certainly subclass Customer and get around it that way but that doesn't always work and certainly wouldn't work for us because we actually want to have multiple little class hierarchies hanging off Customer.

Let’s say we have Order and OrderItems. The Order is the root of an aggregate containing OrderItems. In this case we might actually want to do this in the domain by having the Order have a collection of OrderItems. Maybe not for this situation but we often do.In the domain we want to make the association unidirectional though, we can go from a Order to an OrderItem but we don't want to do the reverse association.

Our database design has the ID of the Order as a foreign key in the OrderItem table. Many people see this as a valid database design and so it’s quite possible this is the way you have it done.

The problem is this isn't going to work unless we do one of two things:

Colin,The second issue is a result of putting the Order class in charge of filling the OrderId column in the OrderLines tables.Since it has to do it in a different statement than the one that persists the OrderLine itself, the OrderId must be nullable.Why not just make the OrderLine responsible for it?

I don't understand your first issue (plus jira link is broken), so I won't say anything about that.

The second one is entirely a problem with NHibernate's implementation/design tradeoffs. (I use a custom ORM that handles the scenario correctly, i.e. will cause an order and its orderitems to be saved within the same transaction and not require a nullable foreign key).

Often when you map a component you actually don't want a single class but a little inheritance hierarchy. So an Account might have an InvestmentAim class hanging off it and mapped as a component (so coming from the Account table).

However further domain analysis (v2) shows that there are multiple kinds of InvestmentAim so you want to create a little inheritance hierarchy, without changing the database.

In that situation it'd be nice to be able to create another column in the Account table and then specify that it is the discriminator for the InvestmentAim hierarchy.

Ok, I understand now. The short answer is that its not entirely NHibernate's fault - its a design tradeoff. The long answer:

The basic mechanics of class inheritance with discriminators anywhere in the hierarchy is easy for an ORM to support. The implications are more difficult though, because they have to balance inheritance against the concept of object identity - which requires that each row in the database has to be uniquely identifiable as belonging to one and only one class. My impression is that NHibernate treats object identity as non-negotiable, and within that context, what you ask for is impossible (in .NET or Java).

Inheritance is not always the best solution anyway. Specifically, we have to beware of using inheritance when there is some chance that the object instance may want to switch roles. (Most languages, cannot support switching types).

In the ORM that I use, discriminators can be used for either inheritance (as described by you) or "roles" (which allow switching). The inheritance compromise is that object identity is not strictly enforced (it does exist though).

I don't think NHibernate has the concept of roles, which is a pity, since it is very useful, and allows some of what you would like to achieve without the complications of inheritance.