Ministry of Technology
Show Menu

The Repository Pattern – Keeping it clean

I've read a suite of articles over the last few weeks regarding various different views on how to implement a Repository Pattern. Martin Fowler's website on patterns clearly defines a Repository as a pattern that...

Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.

Several of these articles concern exposing elements of LINQ to SQL, such as an IQueryable from a given data table through a repository. The plus point of this is simplicity - You can obtain the data you need from the IQueryable exposition throughout your application. The downsides are directly related... 

  • Broken separation of concerns - Your query logic is now in your domain rather than the repository
  • Difficult / Impossible to test - Any logic put in the repository can now not be unit tested as it's dependent on the underlying database.

To my mind I like the repository pattern to be completely agnostic to whatever sits behind it so that my application has a common interface to access data whether it's a database, a file or a Web Service. The repository itself will be aware of what it is talking too, but I don't want the application to have to worry about it beyond any required Dependency Injection.

When using a repository data access pattern I like to split this concern in to two halves, so the repository can handle all of the required query logic, such as GetByID(), GetAll() and Delete() methods and then pass them on to an underlying Mapper or Gateway (depending on the underlying data source). By defining an interface for the mapper or gateway you then make all the associated query logic of the repository easily testable by creating a Mock Mapper / Gateway. I can illustrate this strategy better with code.

Here's a simple repository for obtaining Job data...

public class JobsRepository : IJobsRepository
{
private IJobsDataSourceGateway jobsDataSourceGateway;

public JobsRepository(IJobsDataSourceGateway jobsDataSourceGateway)
{
this.jobsDataSourceGateway = jobsDataSourceGateway;
}

public Job GetById(Guid id)
{
return jobsDataSourceGateway.Jobs.Where(x => x.ID == id).First();
}

public JobCollection GetAll()
{
return new JobCollection(jobsDataSourceGateway.Jobs);
}
}

In this instance, I'm passing a gateway to a data source in as a constructor argument. If I want to test the class I can simply mock this requirement using a helper class like this...

public static class MockDataSourceGateways

  public static IJobsDataSourceGateway Jobs()
  {
    return Jobs (
        new Job() { ID = Guid.Parse("8fbb0f25-cd8b-4376-a398-09f04c9a2d05"),
                    Title = "Job1",
                    Description = "Some words about Job 1",
                    Employer = "Orange",
                    Location = "Bristol",
                    SalaryLower = 20, SalaryUpper = 30 },
        new Job() { ID = Guid.Parse("dd0c71ac-6fb9-4a9c-a092-984d8940f8ca"),
                    Title = "Job2",
                    Description = "Some words about Job 2",
                    Benefits = "Great",
                    Location = "London",
                    SalaryLower = 30, SalaryUpper = 40 },
        new Job() { ID = Guid.Parse("d3680942-6b1e-483c-80b8-a3048484bed4"),
                    Title = "Job3",
                    Description = "Some words about Job 3",
                    Location = "Cardif",
                    SalaryLower = 40, SalaryUpper = 45 }
     );
  } 

  public static IJobsDataSourceGateway Jobs(params Job[] jobs)
  {
    var mockGateway = new Mock();
    mockGateway.Setup(x => x.Jobs).Returns(jobs.AsQueryable());
    return mockGateway.Object;
  } 
}

The actual implementation of the IJobsDataSourceGateway interface is a LINQ to SQL data access class simply exposing IQueryable for the table. It does nothing else at all so testing here isn't really too much of an issue. My basic implementation looks like this...

public class SqlJobsDataSourceGateway : IJobsDataSourceGateway
{
private DataContext context;
private Table jobsTable;

public SqlJobsDataSourceGateway(string connectionString)
{
context = new DataContext(connectionString);
jobsTable = context.GetTable();
}

public IQueryable Jobs
{
get { return jobsTable; }
}
}

This is obviously quite a simple case using LINQ to SQL, this could equally be a simple mapper returning base data from a service API or a limited set of calls to base SQL. The key is keeping as much as possible separated between the query logic in the repository and the direct data access itself while balancing it with performance issue dependencies related to your ORM solution (or lack thereof) of choice.