我的问题是:
>实体应该知道域中的工厂,存储库,服务?
>应该知道域中的服务?
另一件令我心烦的事情是,当我想要添加和实体到集合时,如何对待集合.
假设我正在开发一个简单的CMS.在CMS中,我有一个包含标签实体的文章实体和标签集合.
现在,如果我想添加一个与新标签的关系.什么是更好的方法呢? (PHP中的示例)
$article->tags->add(TagEntity); $articleRepository->save($article);
或者我可以做一个服务.
$articleService->addTag($article,TagEntity);
你怎么看?
谢谢.
工厂和存储库只是用于实体的创建和持久化的助手.大多数时候,他们在真正的问题领域只是没有类比,所以从工厂和仓库到服务和实体的引用根据域逻辑是没有道理的.
关于你提供的例子,这是我如何实现的
$article->addTag($tag); $articleRepository->save($article);
我不会直接访问基础集合,因为我可能希望文章在添加到集合之前,在标签上执行一些域逻辑(强制约束,验证).
避免这个
$articleService->addTag($article,$tag);
仅使用服务来执行不属于任何Entity概念的操作.首先,尝试将其适用于实体,确保它不适合任何.然后只能使用一个服务.这样你不会得到贫血域模型.
更新1
埃里克·埃文斯的“领域驱动设计:解决软件中心的复杂性”的报价书:
SERVICES should be used judicIoUsly and not allowed to strip the
ENTITIES and VALUE OBJECTS of all their behavior.
更新2
有人下了这个答案,我不知道为什么.我只能怀疑原因.它可以是实体和服务之间的引用,也可以是示例代码.嗯,我不能做很多关于示例代码,因为我的意见是基于我自己的经验.但是,我对引用部分做了更多的研究,这里是我想出来的.
我不是唯一认为从实体引用服务,存储库和工厂不是一个好主意.我在这里发现了类似的问题:
> Is it ok for entities to access repositories?
> DDD – Dependecies between domain model,services and repositories
> DDD – the rule that Entities can’t access Repositories directly
> DDD Entities making use of Services
还有一些关于这个问题的好文章,特别是这个How not to inject services in entities,也提出了一个解决方案,如果你迫切需要调用一个名为Double Dispatch的实体的服务.这里是一个从PHP移植到PHP的文章的例子:
interface MailService { public function send($sender,$recipient,$subject,$body); } class Message { //... public function sendThrough(MailService $mailService) { $subject = $this->isReply ? 'Re: ' . $this->title : $this->title; $mailService->send( $this->sender,$this->recipient,$this->getMessageBody($this->content) ); } }
因此,您可以看到,您没有引用MessageService实体中的MailService服务,而是作为参数传递给实体的方法.在http://devlicio.us/的评论文章中,本文作者“DDD: Services”提出了同样的解决方案.
我也试图看看埃里克·埃文斯在他的“领域驱动的设计:解决软件中的复杂性”一书中所说的这一点.简要搜索后,我没有找到确切的答案,但是我发现一个实体静态地调用该服务的例子,那就是没有引用它.
public class BrokerageAccount { String accountNumber; String customerSocialSecurityNumber; // Omit constructors,etc. public Customer getCustomer() { String sqlQuery = "SELECT * FROM CUSTOMER WHERE" + "SS_NUMBER = '" + customerSocialSecurityNumber + "'"; return QueryService.findSingleCustomerFor(sqlQuery); } public Set getInvestments() { String sqlQuery = "SELECT * FROM INVESTMENT WHERE" + "BROKERAGE_ACCOUNT = '" + accountNumber + "'"; return QueryService.findInvestmentsFor(sqlQuery); } }
以下说明如下:
Note: The QueryService,a utility for fetching rows from the database
and creating objects,is simple for explaining examples,but it’s not
necessarily a good design for a real project.
如果您看到上面提到的DDDSample项目的源代码,您将看到实体没有任何引用,除了模型包中的对象,即实体和值对象.顺便说一下,DDDSample项目在“域驱动设计:软件核心应对复杂性”一书中有详细描述…
此外,我想和你分享一件事也是一个类似的讨论
domaindrivendesign Yahoo Group.这个message的讨论引用了埃里克·埃文斯关于引用存储库的模型对象的主题.
结论
总而言之,参考服务,实体库和工厂不是很好.这是最接受的意见.即使存储库和工厂是域层的公民,它们也不是问题域的一部分.有时(例如在维基百科的DDD文章中),域服务的概念称为Pure Fabrication,这意味着类(Service)“不代表问题域中的概念”.我宁愿将工厂和存储库称为纯粹制造,因为埃里克·埃文斯(Eric Evans)在他的书中提到了有关服务概念的其他内容:
But when an operation is actually an important domain concept,a
SERVICE forms a natural part of a MODEL-DRIVEN DESIGN. Declared in the
model as a SERVICE,rather than as a phony object that doesn’t
actually represent anything,the standalone operation will not mislead
anyone.
根据上述说法,有时候从你的实体调用服务可能是一个理想的事情.然后,您可以使用Double Dispatch方法,以便您不必在Entity类中保留对Service的引用.
当然,总是有人不喜欢Accessing Domain Services from Entities文章的主流意见.