Domain driven design has become a very prominent way to develop software. A central domain model is used by all participants. To the end user it is a representation of the real world. To the coder the model is a class model, working with the real domain world boils down to instantiating objects from these classes. In meetings we find ourselves often discussing a Visual Studio class diagram. Nothing fancy, but it works quite well. The domain user recognizes her entities, their data and behavior, the developer recognizes his classes, properties and methods.
In DDD these domain objects interact with the database through repositories. The methods of a repository return (sets of) domain objects as read from the database or accept domain objects which will be persisted to the database. nHibernate is a wonderful tool to build these repositories. All you have to do is define a mapping between domain classes and databases tables and nHibernate will take care of the tedious sql stuff. As a bonus you get a nice API for querying.
But there are reasons not to use nHibernate.
- Learning curve. In some teams the jump from sql scattered between the tags of a web page, stored procedures and wherever else to an O/R mapper is to big. Getting all database access together in one assembly is a big step forward on itself.
- Database server specific functionality. In a current project we are using SQL 2008 because we need the specific geographical functionality. At the time we started the project SQL 2008 was not feature stable yet, the nHibernate support was not quite there either and the specific geographical queries would require a lot of sql passthrough. Combined with point 1 it was obvious we were going to do the sql ourselves.
- Restricted permissions on the application hoster. To do its magic nHibernate uses a lot of reflection. Reflection requires more priviliges than the partial trust many a shared hoster does offer. I tried to get nHibernate to work under partial trust but did not succeed. Perhaps it is possible with nHibernate 2.0
But not using nHibernate does not mean you cannot work in DDD style. Using the repository pattern is still the way to go. In the remainder of this post I will briefly demonstrate that and will focus on lazy loading. This is a programming aspect of domain objects which makes life for you as a coder so much easier and your code such a lot clearer and easier to maintain. Given the domain model of last figure, it is clear to code like this
When you need to think about the database stuff behind this things get complicated. The order has to be read from an orders table. The orderdetails are read from another table and for every orderline a row has to be read from the articles table. The only way to get this working is by implementing lazy loading. That is read the data from the database when it is requested, not any earlier than that. With nHibernate you get lazy loading for free. We had to do it ourselves. The solution presented here works well for us. But don’t take it for granted, we have to jump through some hoops. Feel free to comment. Anything to simplify the matter will improve our code.
The domain layer itself should suffer from persitance ignorance. That is it should be completely unknown to the domain object itself how and where it it persisted. The concession I have to make is that the domain object cannot ignore the fact that it is persited in some way and that it does have a certain price to retrieve data. To express this I define delegates with the domain classes , that is the signature of methods which will actually retrieve a domain object.
Take the article class
This delegate is used by the order line class to lazyload an article
The article of the orderline is instantiated as a null object. The first time the article is requested the static delegate property is fired to actually retrieve the article. To do that I need the article id. Which is passed in the constructor but not exposed by the domain object. In case you need it it is requested as orderLine.Article.ID
Likewise the orderlines define a delegate, this one expresses that orderlines come in a set, all belonging to the same order.
This delegate is used in the Order domain class
Using these static delegates we can have lazy loading. For the delegates to work they have to be wired up. Before doing that we have to take a look at implementing the repositories.
This demo project has three repositories: Orders, Articles and Customers. I will not dive into the details of the implementation, you can find those in a previous post. What is important for now are the signatures of the repository methods. This is the OrderRepository.
The last method is quite interesting as it’s signature matches the OnRequestOrderLines delegate. It is the one which has to be wired to the Order’s RequestOrderLines property.
The repository has a helper class Lazyloaders which exposes all lazy loading methods as static methods.
A repository object has very real instance members such as the connection to the database. It is not desired to have such a connection as a static member of the repository class. So it’s not possible to declare these static lazyloader members as members of the repository. That’s the main reason for the helper class.
The lazyloaders have to be static as they are wired to a static property of the domain objects. This wiring up is done in the static constructor of the repository. A static constructor is fired only once, at the moment in your code the class is actually used the first time.
In the static constructor of the repository the domain class is given an implementation of the lazyloaders. And now I am there and do have lazy loading for my orders and orderlines.
It works. There are some things I don’t quite like
- The domain objects have to expose public delegate properties. These are needed by the repositories but for anything else they can be confusing.
- The orderrepository has to expose the GetOrderLines method. This method makes no sense to any code outside the repositories. There are no orderlines without an order. The only way to access orderlines should be through their order. The lazy loading will return the orderlines. Lazy loading does need this method. Nobody else does. Any suggestion to hide this method is appreciated.
- I have to keep the article ID in the orderline. It is hidden and only required for the lazy loading. But I don’t like having an article Id and an Article in my orderline object.
But as a whole this solution is quite satisfying for us. You might wonder whether it performs well. When the simple loop we started with executes a lot is happening behind the scenes.
For every iteration in the loop an article repository is created and a database query is performed. When it comes to the actual performance the only thing which really counts is database access. Don’t worry about instantiating POCO’s, do worry about complex sql. That’ s where the real bottleneck is. In this case I’m firing a series of short and simple sql statements. The ado.net connection pooling will ensure the multiple subsequent connections to the DB will be fast. And in real life the code presented here works fast and smooth.
The real plus lies in the real part of the application, where the code does things all kind of things with the domain objects. And never, ever has to worry when or how the data is read into the domain object.
I still do miss nHibernate. Writing all the sql, and especially maintaining it, is tedious and can be pretty boring. Except for the geographical stuff. But to all other parts of the application it is transparent whether the repositories are hand work or implemented by an O-R mapper.
I do hope to have made clear that you main goal should be coding against a domain model, and not using an O-R mapper. And I do hope you have suggestions to make my life even easier.