问题
Take this example. A Supervisor domain class exposes a method GetUnderlings(DateTime from, DateTime to) which will return all those people supervised by the given supervisor in the given period.
The method should go here for pleasing semantic reasons. But should go somewhere else for DDD purity. That's because I'm assuming to implement the method one would need to use a Repository, which it seems wrong to embed inside the Domain Entity. In that case, the method should go on a Respository or Service - GetUnderlings(Supervisor supervisor, DateTime from, DateTime to)
How do others handle this scenario?
EDIT: I think the forces can be described like this: according to OO principals, I want the public interfaces of my entities to expose a rich set of business-oriented functionality. BUT according to DDD implementation principals, the implementations of such methods might best be located elsewhere. In a service, for example.
How can this apparent conflict be resolved? The ways I can see are:
- have the entity have a reference to the service, or service interface
- Always make the client go to the service, not to the entity directly (result: loss of coherence, and totally not cool from an OO perspective)
- use "Domain Events" (?)
- use some AOP trick to delegate implementation of a method to the service.
回答1:
If Supervisor
is an Aggregate Root
it is valid to return Underlings
list from Supervisor
but just READONLY collection because Underlings
shold be modified by Supervisor
to apply domain rules and invariants to the modify action. (basic rule not only in DDD, is just well OOP design)
Underlings
seems like a history entity
. In most of the cases (I do not have enough context info to afirm this in your case) history entities
are not aggregate roots
and ONLY aggregate roots
have repositories.
Keep in mind that if the retrieve of Undelings
is for UI
(not to apply an action with rules and invariants) you do not need to care about aggregate roots
, entities
, etc, because you should apply CQRS and use view services to retrieve plain data (1st normal form, not aggregate roots
) to show it to the user. When an action is trigger by the user UI
you need to check rules (that means apply DDD); you retrieve Supervisor
from Repository
, check Underlings
( remember, readonly collection) to take decissions, apply the action and save changes.
回答2:
Supervisor should have a collection of Underlying if they belong to the Supervisor aggregate.
like
class Supervisor {
private Collection<Underlying> underlyings;
}
Then the GetUnderlings(DateTime from, DateTime to) is filtering the undlyings. This is fine.
But if there are too many underlyings belonging to a Supervisor, this solution is not friendly to performance. In this case, I'd like to use make Underlying an aggregate root and use it's Repository to retrieve the result like:
interface UnderlyingRepository {
Collection<Underlying> GetUnderlings(Guid supervisorId, DateTime from, DateTime to);
}
The client(maybe a MVC controller) invokes the repository directly. Then the problem is how to protect the invariants of addUnderlying which used to be protected by the Supervisor aggregate. You could use either DomainService or DomainEvents.
The solution above is based on traditional DDD architectural model. Like @jlvaquero said, you could use CQRS instead.
回答3:
A common approach is to expose all Supervisor
's Underlings
as read-only collection. And if you need to implement a method that filters them by date range you simply add this method to class Supervisor
as GetUnderlings(DateTime from, DateTime to)
and everything works.
If common approach does not work since your Supervisor
has lots of Underlings
, or it is time-consuming to retrieve all these Underlings
, or ... there is a workaround - 'Separated Interface' (PoEAA) pattern by Martin Fowler.
You can define an interface of component that returns Underlings
within specific date range in your Domain Model, but implement it in another layer (e.g. Data Access layer).
In this case your domain entity has no reference to service and it does not exposes any 'Underlings'. All clients that need to get 'Underlings' call service and pass an instance of 'Supervisor' into method and date range.

来源:https://stackoverflow.com/questions/19445011/a-domain-entity-exposing-a-repository-like-method