A domain entity exposing a repository-like method

纵然是瞬间 提交于 2019-12-13 05:39:03

问题


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:

  1. have the entity have a reference to the service, or service interface
  2. 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)
  3. use "Domain Events" (?)
  4. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!