I have been using NHibernate on and off for a while now, especially when I have used a relation database for persistence. It is no secret to most who know me that I’m a massive fan of object databases, particularly db4o.
I like to think I write code which matches my ethos of “Enable rapid change with safety wheels”, where the safety wheels keep me (and the team) on the right track.
When it comes to data access, in particular using Fluent NHibernate, mapping your application domain to your relational database doesnt get much simpler. Fluent NHibernate comes with its own set of safety wheels in the form of a Persistence Specification Testing library.
I think more as a developer than a DBA so I usually start by modeling the domain as a meaningful object model, paying particular attention to the nouns and the verbs used by the domain expert. I like to make user intent (verbs) a first class citizen so I explicitly define the actions user can take as both methods on the domain and at a higher level as Commands that have intent capturing names which encapsulate the required information needed to execute the intent. This results in a system that can handle growth in an evolutionary manner as defined by the client.
With regards to my persistence concerns, as I was saying, I aim to have a level of flexibility in place to allow me react and change quickly without incurring massive time penalties. This is achieved through the following:
- Repository Pattern – providing a layer of persistence ignorance between Business Logic and the data access strategy.
- Database Generation – allowing rapid changes to be made to the database structure to quickly to adapt to changing needs of the application.
- Unit & Integration Testing – Green is Good.
- Persistence Specification- Ensuring our data access layer is behaving as we expect it to.
I am a firm believer in exposing the full power of your data access strategy / Object Relational Mapper (ORM) to the Read related concerns & logic within your application, there is little point in creating your own abstraction on top of the ORM in that regard as you will hide the power through your potentially leaky abstraction.
When it comes to Business Logic, relating to the Write concerns within your application, a general Repository interface (as defined below) helps spare the brains of you application from having to care about what is taking place at a lower level. It also gives a very simple view of the persistence concerns, providing a readability boost subsequently aiding clarity within your Business Logic. This is especially important as you should strive to make you Business Logic very readable where the intent is easy to obtain, ensuring it is not obscured by implementation concerns such as the ORM.
public interface IRepository<T> where T : class
{
void SaveOrUpdate(T instance);
T Find(Func<T, bool> predicate);
IList<T> All(Func<T, bool> predicate);
}
Lets take a glance at what a simple Persistence Specification test looks like:
[Test]
public void CanCorrectlyMapEmployee()
{
new PersistenceSpecification<Employee>(session)
.CheckProperty(c => c.Id, 1)
.CheckProperty(c => c.Forename, "Steven")
.CheckProperty(c => c.Surname, "Holdsworth")
.VerifyTheMappings();
}
What we are doing here is checking that our Employee Mapping correctly Saves and then loads the data for the properties specified in the .CheckProperty( {property}, {value} ) calls. The PersistenceSpecification class handles the instantiation, persistence and loading of the the Employee object based on the information we provide it with, Simples.
Going forward as we define more database mappings, modify existing mappings and relationships, we can start this process by modifying our tests to define what we are trying to achieve. The next step is to then modify our mappings, and our tests will act as the good safety wheels that they are by verifying that our changes are non-breaking changes.
This leads me to the Database Generation. I like to create Session Manager classes when working with NHibernate. These act as my abstraction for creating new instances of ISessionFactory.
I have a special Test Session Manager which also contains the power to generate then populate the database, as well as exposing some nice little configuration options such as refresh the database data each time we create a Session Factory.
CreateSessionFactory returns our Session Factory, configured how we specify. This is useful for being dynamic with your tests, in essence adjusting your training wheels to meet your needs at any given time.
I know I could have used the turnery within the Mappings call ( .Mappings(… ? … : … ) ) here.
public static ISessionFactory CreateSessionFactory()
{
return UseAutoMapping
? Fluently.Configure()
.Database(SQLiteConfiguration.Standard.UsingFile(DbFile))
.Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<Employee>()))
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory()
: Fluently.Configure()
.Database(SQLiteConfiguration.Standard.UsingFile(DbFile))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<EmployeeMap>())
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory();
}
By setting UseAutoMapping to true, I am able to focus on changes to my domain, without having to spend time updating my ‘explicit’ mappings. This helps me drive out new ideas and design, with my tests still being useful in providing feedback during this period of rapid change. I favor explicit mappings as I gain more fine grained control, but I do not wish the sacrifice the development time agility offered by the auto mapping conventions.
Build Schema (below) is called when we create the Session Factory. This gives us the chance to rebuild our database in a way which suits us based on the configuration property RefreshOnEachTest.
private static void BuildSchema(Configuration config)
{
if (RefreshOnEachTest || !_schemaBuiltAtLeastOnce)
{
_schemaBuiltAtLeastOnce = true;
// delete the existing db on each run
if (File.Exists(DbFile))
File.Delete(DbFile);
// this NHibernate tool takes a configuration (with mapping info in)
// and exports a database schema from it
new SchemaExport(config)
.Create(false, true);
}
}
It is a cross between rapid prototyping and testability. When the code starts to take shape the database structure will become more stable and finely tuned, you should have little bother if the database and application evolve independently in that regard.