As we all know, the blue bible states that we should use repository-classes that provide access to our objects, and that encapsulate the actual CRUD operations performed on the datastore. And if you didn’t know this, it’s time to read Eric Evan’s great Domain-driven Design book.

There’s been a lot of fuzz lately about generic repositories. Is a generic repository the right thing to do? It’s goal is to avoid the duplication of code for common operations, such as the saving and deleting of entities. NHibernate provides an API, that makes it very easy to create a generic IRepository class, that satisfies all the basic needs of any repository we would be implementing. You can see how this is done in Ayende’s Rhino.Commons, and on top of that, Davy wrote a great post that explains the how and the why. Read about it here.

There is only one thing I would change about Davy’s example. I would define the interface like this: IRepository<EntityType, IdType>, instead of IRepository<EntityType>. You’ll see in his example, that the Get operation, uses the object type for the ID. Working with object will work perfectly, but I changed it just in favour of type safety.

What does Evans say about repositories?

Let me quote him exactly (Extract from Chapter 6):

“For each type of object that needs global access, create an object that can provide the illusion of an in-memory collection of all objects of that type. Set up access through a well-known global interface. Provide methods to add and remove objects, which will encapsulate the actual insertion or removal of data in the data store. Provide methods that select objects based on some criteria and return fully instantiated objects or collections of objects whose attribute values meet the criteria, thereby encapsulating the actual storage and query technology. Provide REPOSITORIES only for AGGREGATE roots that actually need direct access. Keep the client focused on the model, delegating all object storage and access to the REPOSITORIES.”

Just take a second look at whay I put in bold. Every type of object that needs global access, should have it’s own repository. Which means that the generic repository isn’t the way to go, right?
As most of you already know, I’m very passionate about enforcing the DRY principle. If we create a repository for each object we need to crud data for, then we’ll have some code duplication, since the way we get, save and delete our objects with NHibernate, is always the same. And that’s why we introduced IRepository<T>.

The way I see it

Create a repository for each aggregate root you need access to, and expose it’s methods through an interface. I didn’t say you shouldn’t be using the IRepository, though. I’m just saying to not expose it to your clients. Let them deal only with your specific repositories.

Give me some code, please…

Define your IRepository interface, and implement it in a Repository class. Define an interface per specific repository, and implement it. It’s the specific repository’s implementation that will be accessing the generic Repository class.

The client will only access IUserRepository directly.

public interface IUserRepository
{
	/// <summary>
	/// Retrieves a User by his e-mail address
	/// </summary>
	/// <param name="email">The User's e-mail address</param>
	/// <returns></returns>
	User GetByEmail(string email);
}

public class UserRepository : Repository<User, Guid>, IUserRepository
{
	/// <summary>
	/// Retrieves a User by his e-mail address
	/// </summary>
	/// <param name="email">The User's e-mail address</param>
	/// <returns></returns>
	public User GetByEmail(string email)
	{
		var criteria = DetachedCriteria
			.For<User>()
			.Add(Expression.Eq("Email", email));

		return base.FindOne(criteria);
	}
}

The way the client’s will use the repository, is obvious:

IUserRepository userRepository = new UserRepository();
userRepository.GetByEmail("me@noctovis.net");

As you can see, the code remains clean, readable, short and you’re not repeating any code. If you’re doing basic Save and Delete operations, you’ll have write methods that just wrap the generic Repository’s methods, but still, you won’t have code duplication.

Why I don’t want to expose IRepository<T> directly to clients

There are a few downsides to exposing IRepository<T> to your clients.

1) NHibernate’s Criteria API will be exposed to all your clients. The Repository pattern’s goal is to encapsulate the way you do your data-access. If you offer IRepository to your clients, they are going to have to construct Criteria objects to query what they want. So, again, you’re forcing them to know how the Criteria API works, which conflicts with the goal of a repository.

2) You gain more flexibility. How? Well, if the client would use IRepository directly, he would have to deal with a lot more stuff.

Let me give you an example. Imagine, that it’s a requirement to not actually delete your entities from the database when a user clicks ‘Delete’. You only have to set the entity’s Active-bit to false, and set the ChangedOn and ChangedBy properties. When using IRepository<T> directly, this responsibility shifts towards the client. That’s not good. If you use a custom repository, you can have this handled by UserRepository (from my example above), and have the UserRepository’s delete method, adjust the properties you want, and call the Save-method. Your client doesn’t have to know about this. In his little world, he deleted an entity, and that’s it.

The consequence is again, flexibity. If one day, the product owner says that it’s unnecessary to save deleted Product-entities, you can change your ProductRepository class, make it actually delete your products, without affecting any client-code.

This is just an example, I could come up with more scenario’s where this way of working would be interesting. Just imagine you need to validate entities before saving them to the datastore, or if you need to track changes and store every change a user makes in a seperate datastore…

Note, that it’s not my intention to make you put all the logic to do validation, change tracking, or whatever other requirements you have, in your specific repository classes. If you need to do validation, have that handled by a Specification, and if you need to track changes, have that handled by a different class too. Conclusion: never forget to apply SRP!