Domain driven design 聚合根工厂方法是否可以返回命令而不是发布事件?

Domain driven design 聚合根工厂方法是否可以返回命令而不是发布事件?,domain-driven-design,event-sourcing,aggregateroot,axon,Domain Driven Design,Event Sourcing,Aggregateroot,Axon,在沃恩·弗农(Vaughn Vernon)的《实现领域驱动设计》(Implementing Domain Driven Design)一书中,他描述了聚合根中工厂方法的使用。一个例子是一个论坛聚合根,它有startDiscussion工厂方法,返回一个讨论聚合根 public class Forum extends Entity { ... public Discussion startDiscussion( DiscussionId aDiscussionId

在沃恩·弗农(Vaughn Vernon)的《实现领域驱动设计》(Implementing Domain Driven Design)一书中,他描述了聚合根中工厂方法的使用。一个例子是一个
论坛
聚合根,它有
startDiscussion
工厂方法,返回一个
讨论
聚合根

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

我们启动一个
startdiscussion命令
,由
论坛
处理,
论坛
然后发布一个
讨论开始事件
。外部事件处理程序将捕获
DiscussionStartedEvent
,对其进行转换,并启动
CreateDiscussionCommand
。另一个处理程序将使用
CreateDiscussionCommand
实例化
Discussion
,并且
Discussion
将触发
DiscussionCreatedEvent

或者,我们可以改为:
StartDiscussionCommand
->
CreateDiscussionCommand
->
DiscussionCreatedEvent

我们启动
StartDiscussionCommand
,它将触发一个命令处理程序,并调用
论坛的startDiscussion()方法,该方法将返回
CreateDiscussionCommand
。然后处理程序将分派此
CreateDiscussionCommand
。另一个处理程序接收该命令,并使用该命令实例化
讨论
<代码>讨论
然后将触发
讨论CreateDevent

第一种做法涉及4个DTO,而第二种做法仅涉及3个DTO

你认为哪种做法更可取?还是有其他方法可以做到这一点

你认为哪种做法更可取

命令的动机是指导应用程序更新记录簿。您不希望生成事件的命令非常奇怪

也就是说,如果您的流是

Forum.startDiscussion -> []
Discussion.create -> [ DiscussionCreated ]
人们必然会问,为什么会涉及到该论坛

if (this.isClosed()) {
    throw new IllegalStateException("Forum is closed.");
}
这是一个幻觉——我们正在查看论坛在过去某个任意点的状态,以处理Discussion命令。换句话说,在检查之后,论坛的状态可能会改变,而我们在讨论中的处理过程也不会知道。因此,在验证命令时进行此检查,或者在讨论中检查读取模型也是正确的

(我们从《史记》中得到的一切都是对过去的再现;它必须是过去的再现,这样我们才能在《史记》中阅读。我们现在唯一行动的时刻就是更新《史记》的时刻。更准确地说,在写作的那一刻,我们发现我们对历史的假设过去依然存在。当我们写下讨论的变化时,我们证明了自从我们阅读了数据后,讨论并没有发生变化;但这并不能告诉我们论坛是否发生了变化)

command->command看起来像是一个api兼容性适配器;在旧的api中,我们使用了Forum.startDiscussion命令。我们更改了模型,但仍然支持旧的命令以实现向后兼容性。这一切仍然与请求同步


这是一个真实的事情(我们希望设计能够支持对模型的积极更新,而不需要客户/消费者不断地更新),但这不是一个适合你的流程。

< P>这样的问题的最佳方法是考虑你的聚合(事实上,整个系统)。首先是一个黑匣子,看看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太技术化了。在“现实生活”中,您不创建讨论,而是开始讨论

这意味着论坛的状态参与了讨论的创建。因此,理想情况下(当查看黑盒时),这样的场景将在论坛聚合中实现,并将代表创建事件的事件应用于讨论聚合。这是在其他因素要求论坛和讨论是两个不同聚合的假设下进行的

因此,您并不真正希望命令处理程序返回/发送命令,而是希望该处理程序决定是否创建聚合

不幸的是,Axon还不支持此功能。目前,Axon无法通过其常规API应用属于另一个聚合的事件


但是,有一种方法可以完成它。在Axon 3中,您不必应用事件,您还可以将事件直接发布到事件总线(在事件源的情况下,它将是一个事件存储实现)。因此,要实现此功能,您可以直接发布包含DiscussionCreatedEvent的DomainEventMessage。讨论的ID可以是任何UUID,事件的序列号为0,因为它是讨论的创建事件。

DiscussionCreatedCommand
is
DiscussionCreatedEvent
@ConstantingalBun谢谢我可以选择第一个解决方案,但是没有
CreateDiscussionCommand
DiscussionCreatedEvent
。你真的需要这些吗?我相信
DiscussionStartedEvent
可以sufficient@ConstantinGALBENU你可能是对的。我忘了Axon有聚合因子(它接受第一个事件)我可能会用它来实例化聚合。我会先试用,然后告诉你。谢谢!你试用了吗?它有效吗?仔细阅读代码:
抛出新的IllegalStateException(“论坛关闭”);
你的意思是我应该注入事件吗