Bill McCafferty has released his updates to his NHibernate Best Practices article. The article is fantastic. It is listed as an advanced topic, but if you follow every link and study the topics in his article, the article makes for as great a comprehensive introduction as any… Go read it now.
In the last few posts, we explored very simple concepts from the point of view of DDD and BDD/TDD. In this post, I will put together a very simple testing harness to show how to roll together these concepts in the context of NHibernate.
Begin by reviewing the interface created in Part 3. In that example, the Repository interface is placed at the domain level (Foo.Domain.Repositories.DealerRepository – and no, I don’t use the I in my interfaces). Again, the idea is that a Repository is a domain level concept for managing the lifecycle of Entities. However, the implementation of the persistence mechanism is abstracted from the domain’s point of view.
We will create an implementation of the repository interface for NHibernate. I know there are many ways to implement repositories, but I’m keeping it easy for this example.
One other side note – this example is copying over the same specs/tests from Part 3 for my NHibernate tests, but you could imagine reusing them somehow by injecting in mock or NHibernate implementations of the interface. I really haven’t explored an idea like this, but if somebody has, let me know.
Everything needed for this repository example is in the sample zip file. Go download the zip file now. You will want to focus on the Foo.Data.NHibernate and Tests.Foo.Data.NHibernate projects to see how it all works. Post your questions here.
The database schema is included in the solution if you would like to run that to build the FooDomain database and Dealer table.
This Dealer domain object to table mapping file is located in the Foo.Data.NHibernate project under the DomainMappings folder. The file also contains a component mapping for a phone number Value object (the fax number and contact phone number). Let’s just say that it is important for us to store the phone number broken down into its constituent parts (area code, exchange and SLID) for this example.
In the mapping file, I use of the access=“nosetter.camelcase“ access attribute. This tells NHibernate to set the field values directly rather than through the properties (since no setter is available, as we would like to keep IDs and value object references immutable, as you will see in a moment).
I use a customized version of nDbUnit to allow for rollbacks of transactions rather than explicit delete. My customized version is included in the project file (which is what makes the zip file pretty big) and could use some cleanup if you want to take that on. The project, Tests.Foo.Data.NHibernate contains the xsd used by nDbUnit and you can review the tests to see how it is read into.
My customizations also reset the identity value on the Dealer table
after a DeleteAll is called.
I did not explicitly call flush after the NHibernate save() calls, which
would immediately write the data to the database. The flush is done automatically since
NHibernate has to go to the database to pull the identity value and set
it on the domain object. This is an important point since NDbUnit needs
to have some data to read from the database.
Finally, the tests show how to extract the
NHibernate transaction and pass it to NDbUnit in the GetTransaction()