Repository Interface(s) in Domain-Driven Design
Regarding the contract between the domain and repository, I gather it's best to avoid an all-encompassing generic IReposi开发者_JS百科tory interface with methods such as Create() and Delete()? Unless, of course, it's natural to have these methods available for all the entities I'm working with. Which I imagine is a rare scenario.
Instead, should I create a basic IRepository contract with as many - perhaps "as few" would be more appropriate - common methods (e.g., GetByID()) as makes sense? All repositories could implement the contract and then specialize.
Or is it better to go the route of multiple specialized interfaces, à la ICreatable, IDeletable, IRetrievable, etc.?
I think you should only create an basic IRepository if the methods within the interface are used by all repositories. If you have some repositories that will implement only a part of the methods, than you should go for the more specialized interfaces.
Also the last option will give you more flexibility in adding behaviour later to your repository when needed. If you use the basic IRepository interface and have to add a new method to it, than you will have to update all repositories. If you use the specialized version than you only have to update the repositories to which you want to apply this behaviour.
Generic interfaces are useless, especially in DDD, because they hide otherwise explicit concepts. Imagine your command handler / application service constructor:
public SomeCommandHandler(IRepository customerRepository, IRepository userRepository)
and compare to this:
public SomeCommandHandler(ICustomerRepository customerRepository, IUserRepository userRepository)
The latter is far more explicit and explicitness is what you seek by doing DDD. Moreover, if your repositories have all the same methods, you are probably creating a CRUD data access layer which usually doesn't fit well with DDD.
I think you should use GenericRepository and Specification pattern. This is save you from making a lot of specialized methods in each repository.
I totally agree with Gengzu. I've used this approach in several projects and in my role as a solution architect I want all developers in the team only write necessary and specific code. My experience is that you most of the times want a Delete and Find method etc. The cases where you don't want this shouldn't dictate. Consequences will be that the repositories that need Delete will all have to implement this and this will be redundant code. All these Delete methods could instead have been implemented By a GenericRepository abstract class. That class can work against NHibernate and take a generic entity as T. When you then want to create for example a UserRepository it will inherit from GenericRepository and IUserRepository. Where IUserRepository inherit from IRepository. I will add some code:
public class UserRepository : GenericNHibernateRepository<User, int>, IUserRepository
{
#region IUserRepository Members
public User GetUserByEmail(string email)
{
ICriterion[] query = {Restrictions.Eq("Email", email)};
var list = GetByCriteria(query);
if (list.Count == 0)
return null;
else
return list[0];
}
#endregion
}
public interface IUserRepository : IRepository<User, int>
{
User GetUserByEmail(string email);
}
public interface IRepository<T, ID> where T : IAggregateRoot
{
T GetById(ID id);
List<T> GetAll();
T Save(T entity);
T SaveOrUpdate(T entity);
void Delete(T entity);
void Flush();
}
So what re the benefits. There are huge! As you can see for IRepository interface we only allow IAggregateRoot to have a repository (a DDD guideline). All repositories delete's will be named Delete (no developer will name it Remove etc. and its only implementen at one place, the GenericNHibernate class). If a developer in my team want to create a new repository for an entity the only code is
public class CustomerRepositoryNHibernate : GenericNHibernateRepository, ICustomerRepository {}
Then you get read, delete, find functiuons... you name it. Not Bad.
But another superb benefit if your using Dependency Injection and IoC framework I can just say that with for example Windsor Castle you can create a facility that loads all repositories that implement the IRepository interface and are located in a assemebly (example MyProject.Infrastructure.Data). This gives you extremly boost for your team.
First you developer creates a new repository in MyProject.Infrastructure.Data with on line of code like the example above.
Second at application startup Windsor Castle facility will make it available for injection. So your developer can just continue writing a controller or service class that takes IComnpanyRepository as constructor parameter and its all there.
精彩评论