I've become quite a fan of Eric Evans book, Domain Driven Design. Well, the first half at least. Part of my recent work has been been moving towards the persistence ignorance people in this space push for so much. All this really means is creating a domain layer that is not aware of, and has no dependency on the way it is stored.
Microsoft recently released the ADO.Net Entity Framework which was lots of fun for the ALT.NET crowd because it gave them something new to complain about. One of the things that kept coming up was that Microsoft's new framework wasn't going to support the kind of persistence ignorance many of us are looking for, though apparently it will move towards this in version two. One of the most cited alternatives is NHibernate, the .Net port of Hibernate, the popular Java ORM (object relational mapping) tool.
Though I've used a few ORM tools in the past I haven't used NHibernate, so I thought I would give it a try so I could get the most enjoyment out of the enthusiastic debate over Microsoft's latest toy. When I began using the library I already had some domain objects in place and was pretty happy with how they were sitting. The following are observations made while converting my repositories (data access layer for those not hip with DDD) over to use NHibernate. Note that I am using the library directly, rather than through another layer such as Active Record.
No Dependencies
True to it's word NHibernate lets you create your domain classes without doing any of the following:
- Inheriting from a particular class.
- Implementing a particular interface.
- Attaching any attributes.
- Referencing the NHibernate DLL in any way.
This is really rather refreshing after working with some alternate products that do place these restrictions on you and make it very difficult to move to a different storage mechanism in future. In my case this is particularly useful as I have some data stored in SQL Server, some data accessed via web services and some data on disk but would like to hide that from my application and present it all through the same set of domain classes.
Some Restrictions
While your domain will be free of direct dependencies on the NHibernate library, it does enforce a couple of interesting design decisions on you.
The first thing I ran into was that all of the objects being mapped are required to have a visible default constructor, i.e. one that takes no parameters and is not private. There are sound technical reasons behind this restriction but it did run against the way I had set up my domain. In the end I settled for having my default constructor protected for the mapping and my normal constructor public for the users of the domain. I would like to see an option to specify a mapping to a non default constructor though I'm not sure how feasible this would be to add to the library.
The second thing required of my domain classes was that all of the properties I was mapping had to be virtual. This seems a little odd at first suggestion but it is how you enable NHibernate to perform some of it's more advanced functions such as using proxies and lazy loading. The extra keyword on each property makes negligible difference and is possibly a good idea in some cases anyway. Apparently it is possible to disable this restriction if you are willing to sacrifice some functionality.
So yes, you can write persistence ignorant domain classes, but be prepared to still make some small concessions. In some ways I question whether these requirements are a dependency in disguise but because they are based on the fundamentals of .Net, they are general enough not to bother me. It seems like the only way we are going to get total freedom from the database is by letting go of our tried and trusted statically typed languages.
Flexible Mappings
Most of my ORM experience has been with older or simpler tools where the structure of your objects is really determined by how you lay out your SQL tables. Though it's certainly not unique in this regard, NHibernate gives you plenty of mapping options which let you have distinctly different models for your domain and database objects.
One pleasing feature is the ability to map a custom concept in your domain, such as a class called Kilograms, to one or more SQL columns within a table, such as an integer. This allows you to provide a very strong domain model while still maintaining that automatic mapping back to the basic SQL types. Doing this does require creating a small mapping object derived from IUserType but the pay off is worth it in my case. I need to explore a little more in this area because I suspect much of the IUserType implementation is generic is the simple cases.
Though I haven't used it yet there is also an ability to map part of a SQL table to a separate class to help simplify large objects. The usual example is mapping columns such as Street, Suburb and City to and Address object.
Session Integrity
One of the reasons I hadn't looked into the library was based on faulty assumptions that it would have the same flaws other ORM products I used had. In past projects a common source of weird an unusual behaviour has been the data in the database not keeping up to date with changes that had been made to the data in memory. A simple example is a query to select all items not including new items that have been created but not committed yet.
NHibernate refers to those in memory copies of the data as the current session and solves the problem by automatically flushing any changes to the database whenever it detects these sorts of issues may arise. This means as long as you are using transactions, which you should be, you shouldn't have any issues. You also get a couple of niceties along with it such as generated primary keys being updated as soon as you make NHibernate aware of new objects.
NHibernate, Good Times
Overall a big thumbs up from me and I will continue to use
NHibernate in future. I'm especially interested to see the upcoming version which is expected to include LINQ querying support.
08/10/2008 12:16 AM (UTC -07:00)