C# – How many levels of abstraction do I need in the data persistence layer

abstractioncdomain-driven-designrepository

I'm writing an application using DDD techniques. This is my first attempt at a DDD project. It is also my first greenfield project and I am the sole developer. I've fleshed out the domain model and User interface. Now I'm starting on the persistence layer. I start with a unit test, as usual.

[Test]
public void ShouldAddEmployerToCollection()
{
    var employerRepository = new EmployerRepository();
    var employer = _mockery.NewMock<Employer>();

    employerRepository.Add(employer);
    _mockery.VerifyAllExpectationsHaveBeenMet();
}

As you can see I haven't written any expectations for the Add() function. I got this far and realized I haven't settled on a particular database vendor yet. In fact I'm not even sure it calls for a db engine at all. Flat files or xml may be just as reasonable. So I'm left wondering what my next step should be.

Should I add another layer of abstraction… say a DataStore interface or look for an existing library that's already done the work for me? I'd like to avoid tying the program to a particular database technology if I can.

Best Answer

With your requirements, the only abstraction you really need is a repository interface that has basic CRUD semantics so that your client code and collaborating objects only deal with IEmployerRepository objects rather than concrete repositories. You have a few options for going about that:

1) No more abstractions. Just construct the concrete repository in your top-level application where you need it:

IEmployeeRepository repository = new StubEmployeeRepository();
IEmployee           employee   = repository.GetEmployee(id);

Changing that in a million places will get old, so this technique is only really viable for very small projects.

2) Create repository factories to use in your application:

IEmployeeRepository repository = repositoryFactory<IEmployee>.CreateRepository();
IEmployee           employee   = repository.GetEmployee(id);

You might pass the repository factory into the classes that will use it, or you might create an application-level static variable to hold it (it's a singleton, which is unfortunate, but fairly well-bounded).

3) Use a dependency injection container (essentially a general-purpose factory and configuration mechanism):

// A lot of DI containers use this 'Resolve' format.
IEmployeeRepository repository = container.Resolve<IEmployee>();
IEmployee           employee   = repository.GetEmployee(id);

If you haven't used DI containers before, there are lots of good questions and answers about them here on SO (such as Which C#/.NET Dependency Injection frameworks are worth looking into? and Data access, unit testing, dependency injection), and you would definitely want to read Martin Fowler's Inversion of Control Containers and the Dependency Injection pattern).