How to effectively apply the repository pattern?

Sunday, October 9, 2011

Last week I have been breaking my head on how to effectively apply the repository pattern? There are a lot of different implementations on the web each with different pros and cons.

In the most well-known definition it should encapsulate the business entities from the underlying data infrastructure. Most of the time it sits on top of a data mapping layer which is responsible for mapping your tables to the right classes, usually called entities.

I came across implementations that support CRUD or are just read-only, some are generic, some use a query object pattern, some use the specification pattern, some use LINQ expressions, some support unit of work, some expose the IQueryable<T> interface and some encapsulate advanced queries (the queries you don’t want to be dynamically created).

So there are a lot of choices to make, but what are the right choices? I don’t know to be honest. For example what is better for querying your data: writing your own query object pattern or use LINQ expressions?

The first solution provides a better abstractness of your code, but implies the writing of query translators. This is not what I love doing.

The second solution is nice if your data mapper provides a LINQ provider. This way you can easily pass through the given expression to the provider. In fact this makes it possible to switch between data mappers easily. Most of the bigger O/RM mappers provide LINQ support which makes this a very interesting option. In addition LINQ is known by developers which makes it easier for writing and maintaining your queries.

I have also seen implementations which expose the IQueryable<T> interface. This requires the underlying layer to expose this as well (see solution above-LINQ provider).  This solution makes the repository very lightweight. This is because you will have to need only one generic repository, which covers all your entities and query needs. The business or service layer can directly query the repository.

The downside is that it’s a little bit of a trick. This is because the repo isn’t really giving you back actual data but a query representation. The IQueryable<T> has a deferred execution model which means that you are giving away the control of when the execution takes place. Of course if you own the calling code you’re in control but in my opinion this should stay inside the repository or at least the DAL.

Other important questions are: how do I manage my repositories, do I need a repository for each entity and what kind of functionality it’s encapsulating?

Making a repository class for each entity could be running into a lot of classes which I don’t want. From the domain driven design way of thinking, repositories should only hold aggregate roots. This limits the amount of repositories you need and forces you think about which are roots and which are not.

The most important question is, what does the repository do for me? I surely want it to do me: CRUD (FindByID, Add, Remove, Update), give back all entities (FindAll) and a way to do simple querying (FindBy). I am not sure about if it should do the more advanced queries, because you get stuck on questions like where to put the queries which give back data from different aggregate roots? I don’t know! So maybe it’s better not to include this in the repository, making it possible to create one generic repository for all root aggregates.

The logic for the custom queries should then be encapsulated in their own object. This objects should be created by some query factory or provider. Because each data mapper has their own way of querying data (named queries, HQL, ICriteria, CriteriaOver), you should create a level of abstraction around that.

Last but not least. In case your transaction involves persisting multiple entities, you should rollback everything if anything goes wrong. This is where the unit of work pattern comes into play. The unit of work pattern coordinates the persisting of the made changes and the problems that may occur. Read more about it here. With most data mappers you get this for free so you can easily benefit from it. In that case it’s important that your repository knows about it as well.

In this post, which I created mainly for myself to get everything straight, I answered  a couple of questions I had during my search on how to effectively apply the pattern. There a lot of different implementations out there and you can easily end up implementing the wrong one for your situation. In a second post I will create some code examples based on the results of this post.

Filed Under: Programming, Technology

Comments (2) -

RichardUnited KingdomRichard said:

Hey Marijn,

Just wanted to say your journey through this feels exactly like mine over the last few days...  you're article helped me straighten out a few of the concepts I had in my head.

Thanks!

Richard.

Martijn BurgersNetherlandsMartijn Burgers said:

Hey Richard,

If you have any additions, things I missed, please let me know. Would like to know other opinions as well.

Gr

Martijn

Comments are closed