问题
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.
回答1:
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).
回答2:
At some point you will have to make a call as to what your repository will do with the data. When you're starting your project it's probably best to keep it as simple as possible, and only add abstraction layers when necessary. Simply defining what your repositories / DAOs are is probably enough at this stage.
Usually, the repository / repositories / DAOs should know about the implementation details of which database or ORM you have decided to use. I expect this is why you are using repositories in DDD. This way your tests can mock the repositories and be agnostic of the implementation.
回答3:
I wrote a blog post on implementing the Repository pattern on top of NHibernate, I think it will benefit you regardless of whether you use NHibernate or not.
Creating a common generic and extensible NHiberate Repository
回答4:
One thing I've found with persistence layers is to make sure that there is a spot where you can start doing abstraction. If you're database grows, you might need to start implementing sharding and unless there's already an abstraction layer already available, it can be difficult to add one later.
回答5:
I believe you shouldn't add yet another layer below the repository classes just for the purpose of unit testing, specially if you haven't chosen your persistence technology. I don't think you can create an interface more granular than "repository.GetEmployee(id)" without exposing details about the persistence method.
If you're really considering using flat text or XML files, I believe the best option is to stick with the repository interface abstraction. But if you have decided to use databases, and you're just not sure about the vendor, an ORM tool might be the way to go.
来源:https://stackoverflow.com/questions/1667628/how-many-levels-of-abstraction-do-i-need-in-the-data-persistence-layer