我正在尝试了解DDD以及我无法理解的实体和存储库的问题。
从其他问题来看,我意识到将存储库注入实体是一个坏习惯。但是,当我编写对象时,如何避免注入存储库?
让我们有简单的情况 - 事件和事件应用。这看起来很简单。
$event->add($application);
$eventRepository->save($event);
我相信$ application是一个实体,所以我相信应该有一些$ applicationRepository。
这是否意味着我应该将$ applicationRepository注入$ eventRepository以保存Event实体?像
class eventRepository {
...
public function save(Event $event) {
...
foreach ($event->applications as $app) {
$this->applicationRepository->save($app);
}
...
}
}
我想到的另一种解决方案是:
$eventService->addAplication($event, $application);
class $eventService {
...
public function addApplication(Event $event, Application $app) {
// simple example of validation, something like $event->isAplyable()
if ($event->capacity > count($event->applications)) {
$this->applicationRepository->save($app);
$event->addApplication($app);
}
}
}
一种方法比另一种更好吗?还是我完全搞砸了?
答案 0 :(得分:3)
避免显式调用应用程序存储库的一种方法是使事件存储库保留与给定事件关联的应用程序实例。这基本上是您提出的第一个选项,但是根据您使用的持久性框架,代码可能看起来有点不同。例如,某些ORM通过可达性支持持久性,这意味着如果您持久化事件并且框架找到可以从事件中获得的临时应用程序实例,那么它也会持久存在。在这种情况下,不需要显式的应用程序存储库。
这里的想法是aggregate roots的想法。如果Event
是聚合根并且Application
是组成值对象,则事件存储库必须能够持久化整个对象图,包括关联的应用程序实例。 DDD建议每个聚合根的一个存储库不一定是每个实体。
可能是Event
和Application
都是聚合根(AR)的情况。在这种情况下,不建议在AR之间使用直接对象引用,而是使用标识引用。在这种情况下,您的第二个示例将适用,但形式略有不同。事件服务应该是一个应用程序服务,它托管与事件关联的特定用例。其中之一是添加一个应用程序。区别在于addApplication
方法应该接受事件ID和应用程序ID作为参数,然后从相应的存储库加载。它还能够使用各自的存储库显式地保存事件和应用程序。
请查看Effective Aggregate Design by Vaughn Vernon,了解如何确定您网域中的AR。
答案 1 :(得分:3)
您应该只为每个聚合根提供一个存储库,它们应该独立工作。
因此,我可以看到两种情况,您选择哪种情况取决于业务的执行方式:
如果应用程序和事件是两个不同的聚合根(可以将一个应用程序添加到多个事件中,并且所有事件都应该引用同一个实体吗?),则应使用引用将数据绑定在一起,因此,当您保存事件时,它不会保存应用程序,而只会保存对它所持有的应用程序的引用。
如果事件是聚合根,并且应用程序是生存,死亡和更改的(并且它们共享一致性边界),则事件存储库应该能够将应用程序保存为事件的一部分。现在您没有说明如何保留数据,但ORM可以帮助您。
希望有所帮助。随便问一下。