Simple explanation of the Repository pattern

by Leon Cullens 10. januari 2012 19:44

One of my favorite design patterns is the 'repository pattern'. That's why I decided to dedicate a complete blog post to it. Using the repository pattern in your application can yield a lot of benefits, such as improved testability, easier ways to implement caching and transactions, avoidance of code duplication and it allows you to replace the data source easier (although that probably won't happen too often).

Improved testability

Putting an additional layer between your business logic and the data source instead of querying the data source directly from the business logic layer improves the testability because it allows you to replace this layer (the repository) so that you no longer have to use the database when running unit tests. Instead you can just focus on testing your business logic. This improves the stability, flexibility and performance of your unit tests.

Caching and transactions

Because you have all the logic for querying and persisting in one place, you can easily implement caching in one place. The same goes for transactions. A good combination is to implement the Unit of Work pattern in your repository, so you 'queue' all data that needs to be persisted and persist them all together when you tell the Unit of Work to commit. This allows you to execute multiple actions and commit afterwards, or roll back when an exception gets thrown.

The Repository

There are several ways to implement the repository pattern; one could create one repository for each table in the database (e.g. CustomerRepository, OrderRepository, etc.) or one could create a generic repository that provides generic functionality that applies to all tables.

Below is an example of a repository class made for a specific table (the imaginary Account table) and it's interface:

public interface IAccountRepository
{
    IEnumerable<Account> List();
    Account First();
    Account Last();
    Account Find(int id);

    void Insert(Account account);
    void Update(Account account);
    void Delete(Account account);

    void Commit();
    void Rollback();
}

 

public class AccountRepository : IAccountRepository
{
    public IEnumerable<Account> List()
    {
        // return some stuff
    }

    public Account First()
    {
        // return some stuff
    }

    public Account Last()
    {
        // return some stuff
    }

    public Account Find(int id)
    {
        // return some stuff
    }

    public void Insert(Account account)
    {
        // do some stuff
    }

    public void Update(Account account)
    {
        // do some stuff
    }

    public void Delete(Account account)
    {
        // do some stuff
    }

    public void Commit()
    {
        // commit the changes
    }

    public void Rollback()
    {
        // roll back all changes
    }
}

As you can probably see we could make this repository generic (actually it already is pretty much generic). There have been a lot of discussions on this subject. Many people tried making a generic interface, but very often such an implementation doesn't work in practice. For example: not every entity allows updating or deleting. Some tables have some very specific actions that are executed very often. Trying to abstract this any further by making a generic interface will probably hurt more than it solves.

Using the repository

Now that we've created the repository we can use it to do some great stuff. Below is a very simple and naive example that shows you one usage of the repository:

public class AccountController : Controller
{
    private readonly IAccountRepository _accountRepository;

    public AccountController(IAccountRepository accountRepository)
    {
        _accountRepository = accountRepository;
    }

    [HttpPost]
    public ActionResult New(Account account)
    {
        _accountRepository.Insert(account);

        return View();
    }
}

So what I've done here is create a simple ASP.NET MVC3 project, with a controller that has a constructor that takes a repository class that implements IAccountRepository. We also have a little action method that creates a new account. I've skipped some very important stuff like input validation because that's not the point here. The most important part is the fact that I pass an IAccountRepository object to the controller. Normally this would just be an AccountRepository object, but this could also be a mock/stub (fake) object. This means that we could just inject (this is a hint to 'Dependency Injection') another class that implements IAccountRepository, with methods that actually do... Nothing. This is also called loose coupling.

As you can probably see, this allows us to run unit tests without depending on the database. We could just work with in-memory objects. This yields a lot of benefits: it's faster, it doesn't mess with your database (in fact: you don't even NEED a database anymore) and thereby allowing you to run your unit tests everywhere without having to configure a database everywhere.

To make things every easier we can just use a dependency injection container that maps IAccountRepository to AccountRepository, so we don't have to inject an AccountRepository object manually all the time.

Conclusion

The Repository pattern is one very useful pattern that extremely improves the testability of your application, as well as providing other benefits such as easier ways to implement caching and transactions. By using dependency injection we can decouple the data source from the business logic.

Tags: , , , , , ,

Design patterns

Comments (10) -

Daniel Lidstr&#246;m
Daniel Lidström Sweden
11-1-2012 9:44:28 #

Why go through all this trouble to abstract your data access layer? There's really no point, even for testing. When you mock the data access you're not testing what will actually be executed. You really shouldn't mock the database, for exactly this purpose. Regarding Unit of Work: NHibernate, Entity Framework, RavenDB all implement the pattern already. So there's no need to try to do so yourself. Here's the base class I use for my tests that need to interact with a database: github.com/.../DbTest.cs. I am using RavenDB in memory that works exactly as the one I use for production.

Reply

Leon Cullens
Leon Cullens Netherlands
11-1-2012 17:57:33 #

Not everyone is using EF, NHibernate, etc. ;)

Reply

Craig
Craig Australia
11-1-2012 23:06:21 #

When using NHibernate (and probably EF), I agree with Ayende and others that Repository has no real purpose and ultimately just gets in the way.

Reply

Mike
Mike United States
12-1-2012 9:46:47 #

Good start, but needs more details. One thing I can't yet figure out is this: if there are navigation properties to other entities, who is responsible for loading them?

Reply

steve
steve United States
17-1-2012 6:52:10 #

thanks for this

Reply

Darren
Darren United States
31-1-2012 0:46:34 #

I can't help but wonder if the people who claim that the repository pattern has no point have ever tried test-driven-development.  Unit tests should not require an abstract base class!

Reply

Craig
Craig Australia
31-1-2012 1:12:56 #

Darren, when using NHibernate you can quite easily mock ISession in unit tests.

Reply

Darren
Darren United States
31-1-2012 3:52:48 #

You can mock it, but it shouldn't be the responsibility of the code to do the data retrieval logic AND whatever logic it is meant to do.

Reply

Karl
Karl United States
23-4-2013 16:23:15 #

But all you are doing is simply wrapping the database. What if you had business logic that meant you always had to do x before inserting an account? If you build that into your one implementation of IAccountRepository and then later decided to create a new implementation so you could unit test, you would then have to copy that business logic to your new implementation.

Reply

Vinod R
Vinod R India
11-7-2013 9:24:22 #

Very good example for beginners...

Reply

Pingbacks and trackbacks (3)+

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading

about

Name: Leon Cullens
Country: The Netherlands
Job: Software Engineer / Entrepreneur
Studied: Computer Science 
Main skills: Microsoft technology (Azure, ASP.NET MVC, Windows 8, C#, SQL Server, Entity Framework), software architecture (enterprise architecture, design patterns), Marketing, growth hacking, entrepreneurship

advertisements

my apps