在Vaughn Vernon的Implementing Domain-Driven Design
书中,他描述了在聚合根中使用工厂方法。一个例子是Forum
聚合根,其startDiscussion
工厂方法返回了Discussion
聚合根。
public class Forum extends Entity {
...
public Discussion startDiscussion(
DiscussionId aDiscussionId, Author anAuthor, String aSubject) {
if (this.isClosed()) {
throw new IllegalStateException("Forum is closed.");
}
Discussion discussion = new Discussion(
this.tenant(), this.forumId(), aDiscussionId, anAuthor, aSubject);
DomainEventPublisher.instance().publish(new DiscussionStarted(...));
return discussion;
}
如何在事件采购系统中实现此工厂模式,特别是在Axon?
我相信传统,它可能以这种方式实现:
StartDiscussionCommand
- > DiscussionStartedEvent
- > CreateDiscussionCommand
- > DiscussionCreatedEvent
我们点击StartDiscussionCommand
来处理Forum
,Forum
然后发布DiscussionStartedEvent
。外部事件处理程序将捕获DiscussionStartedEvent
,转换它并触发CreateDiscussionCommand
。另一个处理程序将使用Discussion
实例化CreateDiscussionCommand
,而Discussion
会触发DiscussionCreatedEvent
。
或者,我们可以改为:
StartDiscussionCommand
- > CreateDiscussionCommand
- > DiscussionCreatedEvent
我们触发StartDiscussionCommand
,它将触发命令处理程序并调用将返回Forum
的{{1}}的startDiscussion()方法。然后处理程序将调度此CreateDiscussionCommand
。另一个处理程序接收该命令并使用它来实例化CreateDiscussionCommand
。然后,Discussion
会触发Discussion
。
第一种做法涉及4个DTO,而第二种做法仅涉及3个DTO。
有关哪种做法应该首选的任何想法?还是有另一种方法可以做到这一点吗?
答案 0 :(得分:4)
解决此类问题的最佳方法是首先将您的聚合(实际上是整个系统)视为黑盒子。只需看看API。
Given a Forum (that is not closed),
When I send a StartedDiscussionCommand for that forum,
A new Discussion is started.
但也
Given a Forum that was closed
When I send a CreateDiscussionCommand for that forum,
An exception is raised
请注意,您建议的API太技术化了。在“现实生活”中,你不会创建一个讨论,而是开始讨论。
这意味着论坛的状态参与了讨论的创建。理想情况下(当查看黑匣子时),这样的场景将在论坛聚合中实现,并应用表示讨论聚合的创建事件的事件。这是因为其他因素要求论坛和讨论是两个不同的聚合。
因此,您并不希望Command处理程序返回/发送命令,您希望该处理程序决定是否创建聚合。
不幸的是,Axon还不支持此功能。目前,Axon无法通过常规API应用属于另一个聚合的事件。
然而,有一种方法可以完成它。在Axon 3中,您不必apply
一个事件,您也可以直接将其发布到事件总线(在事件采购的情况下将是事件存储实现)。因此,要实现此功能,您可以直接发布包含DiscussionCreatedEvent的DomainEventMessage。讨论的ID可以是任何UUID,事件的序列号是0,因为它是讨论的创建事件。
答案 1 :(得分:-1)
有关哪种做法应该首选的想法?
命令的动机是指示应用程序更新记录簿。你不希望产生一个事件的命令是非常奇怪的。
也就是说,如果您的流程是
Forum.startDiscussion -> []
Discussion.create -> [ DiscussionCreated ]
有人会问为什么论坛会参与其中?
if (this.isClosed()) {
throw new IllegalStateException("Forum is closed.");
}
这是一种幻觉 - 我们在过去的某个任意点处查看论坛的状态来处理讨论命令。换句话说,在此检查之后,论坛的状态可能会发生变化,我们在讨论中的处理将无法知晓。因此,在验证命令时或通过在Discussion中检查读取模型来进行此检查是正确的。
(我们从记录册中得到的所有东西都是过去的代表;它必须是,为了我们已经在记录册中供我们阅读。我们现在行动的唯一时刻是我们更新了记录册的那一点。更准确地说,它是在我们发现我们已经发现过去的假设的时刻仍然存在。当我们将更改写入讨论时,我们证明了讨论自从我们阅读数据以来没有改变;但这并没有告诉我们论坛是否已经改变了。
什么命令 - >命令看起来像是api兼容性适配器;在旧的API中,我们使用了Forum.startDiscussion命令。我们更改了模型,但继续支持旧命令以实现向后兼容性。它仍将与请求同步。
这是真实的事情(我们希望设计支持对模型的积极更新,而不要求客户/消费者不断更新),但它不适合您的流程。