Convention over configuration

August 4, 2009

Ruby on rails makes heavily use of the Convention over configuration design paradigm. At the moment it is getting a hot topic in the .NET world too. If you are like me and are lazy and hate doing repetitive work, then you should really check it out, if you are not, you should check it out anyway. In my opinion, when properly applied, convention over configuration can save developers a lot of time and effort.

So what is convention over configuration? One of the best examples can be found in the Fluent Nhibernate project. This is a project that facilitates configuring Nhibernate, without the use of those horrible XML mapping files. One feature of this project is Automapping, which is convention over configuration in its purest form. I will just assume you are familiar with the Nhibernate mapping files, if not, you can read all about in the Nhibernate documentation.

Let’s say you have a domain entity Customer:

public class Customer
{
    public virtual int Id { get; private set; }
    public virtual string Name { get; set; }
}

 

And you would have your corresponding Nhibernate mapping file as we know it all too well:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
  <class name="NHibernate.Examples.Customer, NHibernate.Examples" table="Customer">
    <id name="Id" column="Id" type="Int">
      <generator class="identity" />
    </id>
    <property name="Name" column="Name" type="String" length="40"/>
  </class>
</hibernate-mapping>

 

I used to have mapping files like these all over the place. Writing these mapping files gets really boring and mistakes are easily made and hard to debug. The Automapping feature makes all of this a thing of the past. With Automapping it is possible to provide, for example a namespace, and automatically map all the entities in that namespace to a database. This mapping takes place based on conventions. One such convention can be that an entity’s property with the name Id always maps on an identity field named Id in the database. Or that a many to one relationship always maps on to a certain column. For example Order –> Customer automatically maps to the column Customer_id in the Order table. With the fluent nhibernate automapping it is also really easy to overwrite the default conventions by providing your own. Off course, this is not very useful when mapping to a legacy database, but in most of the green field Nhibernate projects it can be applied successfully. So in this case, in stead of configuring all of the mappings in XML files, we have certain conventions that allow Fluent nhibernate to assume entities are mapped to the database in the same consistent way.

 

Another good example of convention over configuration is the registration of services with an Inversion of Control container. In a lot of TDD based systems, there are loads of interfaces that only have one implementation. So why have to register all of these services one at the time? Why not just say to the container automap all services that fulfill a certain requirement, like being in a namespace or deriving from a certain base interface. Structuremap has this functionality out of the box. Unity doesn’t have this yet, but Derek Greer has created a Unity extensions to provide this, which he describes in this blog post.

So instead of doing this:

Container.RegisterType<ICategoryRepository, CategoryRepository>();
Container.RegisterType<ICustomerRepository, CustomerRepository>();
Container.RegisterType<IOrderRepository, OrderRepository>();

It allows you to do stuff like this:

Container
    .AddNewExtension<ConventionExtension>()
    .Using<IConventionExtension>()
    .Configure(x =>
                  {
                     x.Conventions.Add<ImplementationConvention<IRepository>>();
                     x.Assemblies.Add(Assembly.GetExecutingAssembly());
                   })
    .Register();

Which automatically maps all the services deriving from IRepository in the executing assembly. This can save you a lot of work, personally I always forget to add the service I made to the container. However, it also involves a lot of black magic. The wiring gets done under the hood, so how you debug a problem when something goes wrong? Well you will need some diagnostics. For example, it is possible to have Fluent Nhibernate spit out all the mapping xml file that are used under the hood, which makes it easy to debug. So when diagnostics are provided, convention over configuration can be really useful and is a welcome addition to my bag of tricks.

Comments

comments powered by Disqus