Repositories 101

time to read 4 min | 713 words

I got this question in an email today, and I think that it would make a good post. I broke it into two parts:

We are moving from Delphi (RAD way) to  real OO in C# + NHibernate.  I've reading about DDD. Now I'm kind of lost about Repositories + NHibernate. I found code that uses a Repository<T> class, which I liked at first, but after a while I wondered... how can i test it ? or, tomorrow we can change from nhibernate to other thing...

Let us talk about my Repository<T>, it is a static class, so it can't be mocked, but it is fully testable, since it uses Windsor to get the implementation of the IRepository<T> interface, of which there are several. Now consider the following code:

public ICollection<Customer> GetPreferredCustomers()
  return Repository<Customer>.FindAll(Where.Customer.IsPreferred = true);

Is it testable? Yes, I can replace the internal implementation of the Repository<T> with my own, in this case, a mock. The test would look something like:

public void GetPreferredCustomerWillForwardQueryToRepository()
   //already setup with a fresh mock that it will return
   IRepository<Customer> customersRepository = IoC.Resolve<IRepository<Customer>>();
     .Constraints(Criteria.Contains("IsPreferred = True"), Is.Anything). Return(null);

As you can see, it is not hard, but neither is it trivial. And I am not testing that the query works. For this types of situations, I am using in memory database and test against known data. I feel that it gives me as much value, and it doesn't hinder the tests.

How can I build a nice repository that could allow  change its "core", well, from nhibernate to what else.. I thought about an IRepository<T> interface, and than have a NHRepository : IRepository<T>, and in my code I referece to IRepository, and not to NHRepository...   am in in the right way ?

If you really like, you can do it by taking an interface like my IRepository<T> and use that, just stripping all the NHibernate specific parts. The reason I don't like it is that it is a false promise. You won't be able to easily switch to another ORM easily. Each ORM does it magic differently, and trying to move from NHibernate to LLBLGen (or vice versa), to make a simple example, is not going to be "let us just change the repository implementation" and that is it.

That would actually be the easy part, the far harder issue would be to make sure that logic that relies on the existance of Unit of Work (or auto wiring of reverse dependencies, to take something that NHibernate does not do), is much harder. You can limit yourself to simple CRUD operations, in which cases the differences are neglible in most scenarios, but that is missing out a lot, in my opinion.