问题
I've just started working with DDD, so maybe this is a silly question...
Is it ok for an entity to access a repository (via some IRepository interface) to get a value at runtime? For example, I want to enforce a "default" selection for a property:
class Person {
private Company _employer;
public Company Employer {
get { return _employer; }
set {
if(value != null) {
_employer = value;
} else {
_employer = employerRepository.GetDefaultEmployer();
}
}
}
...
}
My question is whehter doing something like this is a horrible violation of DDD principles. And if it isn't, my next question would be what it the best way to provide the repository to use? Should it be supplied when the Person object is created?
Thanks, P
回答1:
it's not a horrible violation of DDD it's a horrible violation of... well... it's just plain horrible (i say this tongue in cheek) :).
First off, your entity becomes dependent on having a repository... that's not ideal. Ideally you'd want to have your repository create the Person and then assign it everything it needs to be effective in the current domain context.
So when you need a Person, you'll go personRepository.GetPersonWithDefaultEmployer() and get back a person which has default employer populated. The personRepository will have a dependency on an employerRepository and use that to populate the person before returning it.
PersonReposotory : IPersonRepository
{
private readonly IEmployerRepository employerRepository;
//use constructor injection to populate the EmployerRepository
public PersonRepository(IEmployerRepository employerRepository)
{
this.employerRepository = employerRepository;
}
public person GetPersonWithDefaultEmployer(int personId)
{
Person person = GetPerson(personId);
person.Employer = employerRepository.GetDefaultEmployer(personId);
return person;
}
}
回答2:
The sort-of answer to your question is the standard: It depends .
As a rule of thumb, don't ever do this. Keep your entities without references to repositories.
[putting practical hat on] In some extremely rare cases where you have a very, very, very good reason for doing this, add a big comment explaining why you're doing it and do it: add the reference or use Double Dispatch to pass the repository[hat off]
Also, if you wish to follow DDD principles, it's highly recommended you have access to a domain expert and an iterative process of development ( see Eric Evans - what i've learned since the book ).
With your domain expert you should define bounding contexts and most importantly the aggregates and their aggregate roots and their entities and value objects. Going down the DDD road is not easy at first but the rewards are worth it.
A few things regarding the code you posted:
It's not recommended to have public setters on your entities. Use methods instead that express the intent better.
If a person instance is created without initializing the _employer field, the getter for the Employer property will return null. If you then set the value of the Employer property to null, the next call to the getter will return a non-null value. This is probably unexpected by the users of your class.
The caller setting the Employer of the Person (either by public setter or public method) should know the exact Company instance it wants to set, even if it's the default one. Maybe the caller can have the reference to the repository.
Depending on your concrete domain, the Company might be a value object. In that case instead of initializing the _employer with null you could initialize it with the default value for the value object. This could be the case if you only have very few companies (1-2) and they are immutable and don't have specific behavior.
回答3:
I think it is easy to say an entity shouldn't be aware of repositories, but hard to put it into practice. Especially when an aggregate gets a big collection of vo's inside it, we have to refactor it and delegate operations like add to some domain service that actually acts as a repository to avoid the overhead of loading entire collection into memory.
But I don't think it's reasonable to let entities know of repositories. If we have to , then use domain services instead.We should also consider if a repository violates the Single Responsibility principle -- it should have been thought of as a Collection of aggregate roots, not a normal factory.
回答4:
Is it really what is recommanded doing ddd ?
And what if you don't have an in-memory repository, but a relationnal database for your repository, and you want to get 1000 persons with their employer ? Are you going to make 1000 queries through employerRepository call...?
I would use NHibernate or any ORM to help implement personRepository. With NHibernate, I will use Hibernate Query close to this one : "from Person join fetch Employer" which will load an "Employer" instance in each "Person" instance, with only one SQL query.
Is it a violation to DDD ?
回答5:
First of all, I think the entity itself and how to assembly the entity are 2 duties in fact. So ideally it is better to distribute them into different class. But that's depends on too.
来源:https://stackoverflow.com/questions/827670/is-it-ok-for-entities-to-access-repositories