聚合根工厂方法 return 可以是命令而不是发布事件吗?
Can an Aggregate Root factory method return a command instead of publishing an event?
在 Vaughn Vernon 的 Implementing Domain-Driven Design
书中,他描述了在聚合根中使用工厂方法。一个例子是 Forum
聚合根具有 startDiscussion
工厂方法,该方法 return 编辑了 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
。另一个处理程序将使用 CreateDiscussionCommand
实例化 Discussion
并且 Discussion
将触发 DiscussionCreatedEvent
.
或者,我们能否改为:
StartDiscussionCommand
-> CreateDiscussionCommand
-> DiscussionCreatedEvent
我们触发 StartDiscussionCommand
,这将触发命令处理程序并调用 Forum
的 startDiscussion() 方法,该方法将 return CreateDiscussionCommand
。处理程序然后将调度此 CreateDiscussionCommand
。另一个处理程序接收命令并使用它来实例化 Discussion
。 Discussion
然后会触发 DiscussionCreatedEvent
.
第一种做法涉及4个DTO,而第二种做法只涉及3个DTO。
有没有关于应该首选哪种做法的想法?或者还有其他方法吗?
Any thoughts on which practice should be preferred?
命令的动机是指示应用程序更新记录簿。您不希望产生事件的命令非常奇怪。
也就是说,如果你的流量是
Forum.startDiscussion -> []
Discussion.create -> [ DiscussionCreated ]
有人肯定会问为什么要参与论坛?
if (this.isClosed()) {
throw new IllegalStateException("Forum is closed.");
}
这是一种错觉——我们正在查看论坛在过去任意时间点的状态以处理讨论命令。换句话说,在这个检查之后,论坛的状态可能会改变,而我们在讨论中的处理就不知道了。因此,在验证命令时或通过在讨论中检查读取模型时进行此检查同样正确。
(我们从记录簿中得到的一切都是过去的代表;它必须是,以便已经在记录簿中供我们阅读。我们在当下行动的唯一时刻是我们更新记录簿的那一点。更准确地说,是在写入的那一刻,我们发现我们对过去所做的假设是否仍然成立。当我们将更改写入讨论时,我们正在证明讨论自从我们读取数据以来没有发生变化;但这并没有告诉我们论坛是否发生了变化。
command->command 看起来像一个 api 兼容适配器;在旧的 API 中,我们使用了 Forum.startDiscussion 命令。我们更改了模型,但继续支持旧命令以实现向后兼容性。它仍然会与请求同步。
这是真的(我们希望设计支持对模型进行积极更新,而不需要 clients/consumers 不断更新),但它不适合您的流程。
解决此类问题的最佳方法是首先将聚合(实际上是整个系统)视为黑盒。看看 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 技术性太强。在 'real life' 中,您不创建讨论,而是发起讨论。
这意味着论坛的状态参与了讨论的创建。所以理想情况下(当查看黑匣子时),这样的场景将在论坛聚合中实现,并应用一个代表讨论聚合创建事件的事件。这是假设其他因素要求论坛和讨论是两个不同的集合。
所以您并不是真的希望命令处理程序 return/send 命令,您希望该处理程序决定是否创建聚合。
遗憾的是,Axon 尚不支持此功能。目前,Axon 无法通过其常规 APIs.
应用属于另一个聚合的事件
但是,有一种方法可以完成它。在 Axon 3 中,您不必 apply
一个事件,您也可以直接将一个事件发布到事件总线(在事件溯源的情况下,这将是一个事件存储实现)。因此,要实现这一点,您可以直接发布包含 DiscussionCreatedEvent 的 DomainEventMessage。讨论的ID可以是任意UUID,事件序号为0,因为是讨论的创建事件。
在 Vaughn Vernon 的 Implementing Domain-Driven Design
书中,他描述了在聚合根中使用工厂方法。一个例子是 Forum
聚合根具有 startDiscussion
工厂方法,该方法 return 编辑了 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
。另一个处理程序将使用 CreateDiscussionCommand
实例化 Discussion
并且 Discussion
将触发 DiscussionCreatedEvent
.
或者,我们能否改为:
StartDiscussionCommand
-> CreateDiscussionCommand
-> DiscussionCreatedEvent
我们触发 StartDiscussionCommand
,这将触发命令处理程序并调用 Forum
的 startDiscussion() 方法,该方法将 return CreateDiscussionCommand
。处理程序然后将调度此 CreateDiscussionCommand
。另一个处理程序接收命令并使用它来实例化 Discussion
。 Discussion
然后会触发 DiscussionCreatedEvent
.
第一种做法涉及4个DTO,而第二种做法只涉及3个DTO。
有没有关于应该首选哪种做法的想法?或者还有其他方法吗?
Any thoughts on which practice should be preferred?
命令的动机是指示应用程序更新记录簿。您不希望产生事件的命令非常奇怪。
也就是说,如果你的流量是
Forum.startDiscussion -> []
Discussion.create -> [ DiscussionCreated ]
有人肯定会问为什么要参与论坛?
if (this.isClosed()) {
throw new IllegalStateException("Forum is closed.");
}
这是一种错觉——我们正在查看论坛在过去任意时间点的状态以处理讨论命令。换句话说,在这个检查之后,论坛的状态可能会改变,而我们在讨论中的处理就不知道了。因此,在验证命令时或通过在讨论中检查读取模型时进行此检查同样正确。
(我们从记录簿中得到的一切都是过去的代表;它必须是,以便已经在记录簿中供我们阅读。我们在当下行动的唯一时刻是我们更新记录簿的那一点。更准确地说,是在写入的那一刻,我们发现我们对过去所做的假设是否仍然成立。当我们将更改写入讨论时,我们正在证明讨论自从我们读取数据以来没有发生变化;但这并没有告诉我们论坛是否发生了变化。
command->command 看起来像一个 api 兼容适配器;在旧的 API 中,我们使用了 Forum.startDiscussion 命令。我们更改了模型,但继续支持旧命令以实现向后兼容性。它仍然会与请求同步。
这是真的(我们希望设计支持对模型进行积极更新,而不需要 clients/consumers 不断更新),但它不适合您的流程。
解决此类问题的最佳方法是首先将聚合(实际上是整个系统)视为黑盒。看看 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 技术性太强。在 'real life' 中,您不创建讨论,而是发起讨论。
这意味着论坛的状态参与了讨论的创建。所以理想情况下(当查看黑匣子时),这样的场景将在论坛聚合中实现,并应用一个代表讨论聚合创建事件的事件。这是假设其他因素要求论坛和讨论是两个不同的集合。
所以您并不是真的希望命令处理程序 return/send 命令,您希望该处理程序决定是否创建聚合。
遗憾的是,Axon 尚不支持此功能。目前,Axon 无法通过其常规 APIs.
应用属于另一个聚合的事件但是,有一种方法可以完成它。在 Axon 3 中,您不必 apply
一个事件,您也可以直接将一个事件发布到事件总线(在事件溯源的情况下,这将是一个事件存储实现)。因此,要实现这一点,您可以直接发布包含 DiscussionCreatedEvent 的 DomainEventMessage。讨论的ID可以是任意UUID,事件序号为0,因为是讨论的创建事件。