首页 > 解决方案 > DDD => 根聚合中的行为:实例化其他根聚合

问题描述

我有 2 个根聚合:-发票-投诉

而且我有一条规则说:“如果针对发票提出投诉,我将无法删除发票”。

在我对发票汇总的删除行为中,我想检查是否存在投诉,例如:

Complaint complaint = ComplaintRepository.findByInvoiceId(invoiceId);

if(complaint.isOpened) {
throw new Exception("Open Complain...");
}
else{
...
}

我和我的同事对此意见不一。他们对我说,由于投诉不在我的汇总中,因此我无法在我的行为中体现投诉。我的观点是,我不能在 Invoice 类中拥有 Complaint 属性,但是: - 我可以用值对象引用一个(他们对此没问题) - 我可以读取/加载一个实例,因为我没有调用它的行为...

你对此有意见吗?

标签: domain-driven-designaggregateroot

解决方案


从技术上讲,您可以按照您的建议去做:从某个角度来看,如果您通过构造函数注入或方法注入将 ComplaintRepository 接口注入发票,那么您将使发票依赖于双方的合同存储库和投诉,这几乎是允许的。

当您说您无法引用投诉时您是对的,但是您可以在需要运行时将 DDD 工件(例如工厂/存储库/实体)注入到操作中。

但是,您必须问自己的要点是:您真的想要两个不同聚合之间的这种级别的耦合吗?在这一点上,它们是如此耦合在一起,如果没有一个和另一个,它们大多无法运行。

考虑到所有这些,您可能会遇到这样的情况:投诉可能只是发票汇总的一部分(尽管您的发票汇总可能还有其他职责,并且您将开始为“设计小型汇总”目标而奋斗)。如果您考虑一下,这就是不变的“如果针对发票打开投诉,我将无法删除发票”的建议。

如果无论如何将投诉建模为发票汇总的一部分是不切实际的,您还有其他一些选择:

  • 使这些聚合最终保持一致:与其尝试“一次性”删除发票,不如在一次操作中将其标记为已标记为删除。此操作会在您的消息传递机制中触发某种领域事件。然后,此事件“InvoiceFlaggedForDeletion”将检查发票上的投诉。如果您没有投诉,则将其删除。如果您有投诉,则回滚删除标志。

  • 将删除过程放在域服务中。这样,域服务将在适当的时候协调检查投诉和删除发票的工作。这种方法的缺点是您的 Invoice 实体的规则不太明确,但从 DDD 角度来看,这有时是一种可以接受的方法。


推荐阅读