为什么分别表示命令和事件?

时间:2011-02-10 21:13:07

标签: events command cqrs event-driven-design

强调事件的体系结构中的命令和事件之间有什么区别?我能看到的唯一区别是命令通常由系统外的actor来源/调用,而事件似乎是由处理程序和系统中的其他代码提供的。但是,在我看到的许多示例应用程序中,它们具有不同(但功能相似)的接口。

9 个答案:

答案 0 :(得分:129)

可以拒绝命令。

事件已经发生。

这可能是最重要的原因。在事件驱动的体系结构中,毫无疑问,引发的事件代表发生的事情

现在,因为命令是我们想要发生的事情,并且事件是发生的事情,所以当我们命名这些事情时,我们应该使用不同的动词。这会驱动单独的表示。

  

我可以看到命令通常是   由外面的演员来源/调用   系统,而事件似乎是   由处理程序和其他代码提供   一个系统

这是他们分别代表的另一个原因。概念清晰度。

命令和事件都是消息。但它们实际上是独立的概念,应该明确地对概念进行建模。

答案 1 :(得分:5)

此外,除了此处公开的所有答案之外,事件处理程序也可以在收到事件发生的通知后触发命令。

例如,在创建客户之后,您还想初始化一些帐户值等。在您的客户AR将事件添加到EventDispatcher并且CustomerCreatedEventHandler对象接收到此事件后,此处理程序可以触发调度一个可以执行你需要的任何命令等等。

此外,还有DomainEvents和ApplicationEvents。差异只是概念上的。您希望首先调度所有域事件(其中一些可能会生成应用程序事件)。这是什么意思?

在CustomerCreatedEvent发生后初始化帐户是DOMAIN事件。向客户发送电子邮件通知是应用程序事件。

你不应该混合它们的原因很明显。如果您的SMTP服务器暂时关闭,这并不意味着您的DOMAIN操作应受此影响。您仍希望保持聚合的未损坏状态。

我通常在Aggregate Root级别向Dispatcher添加事件。此事件是DomainEvents或ApplicationEvents。可以是两者并且可以是其中许多。一旦我的命令处理程序完成,我回到堆栈中执行命令处理程序的代码,然后我检查我的Dispatcher并调度任何其他DomainEvent。如果所有这些都成功,那么我关闭交易。

如果我有任何应用程序事件,现在是时候发送它们了。发送电子邮件不一定需要打开到数据库的连接,也不需要打开事务范围。

我从原始问题中略微偏离了一点,但同样重要的是要了解事件在概念上的处理方式也不同。

然后你有Sagas ....但是这个问题的范围是WAYYYY OFF:)

有意义吗?

答案 2 :(得分:4)

通过一些例子,特别是Greg Young的演讲(http://www.youtube.com/watch?v=JHGkaShoyNs)后,我得出的结论是命令是多余的。它们只是来自用户的事件,他们按下了那个按钮。您应该以与其他事件完全相同的方式存储它们,因为它是数据,您不知道是否要在将来的视图中使用它。您的用户确实添加了,然后从篮子中删除该项目或至少尝试。您稍后可能希望使用此信息在以后提醒用户。

答案 3 :(得分:2)

它们代表性很强,因为它们代表了截然不同的东西。 正如@qstarin所说,命令是可以被拒绝的消息,并且成功时会产生一个事件。 命令和事件是Dtos,它们是消息,它们在创建和实体时往往看起来非常相似,但从那时起,不一定。

如果您担心重用,那么您可以使用命令和事件作为您的(消息)有效负载的信封

class CreateSomethingCommand
{
    public int CommandId {get; set;}

    public SomethingEnvelope {get; set;}
 }

然而,我想知道的是你为什么要问:D你是否有太多的命令/事件?

答案 4 :(得分:2)

除了上面提到的概念差异之外,我认为还有另一个与常见实现相关的差异:

事件通常在需要轮询事件队列的后台循环中处理。任何对该事件采取行动感兴趣的方通常都可以注册由于事件队列处理而调用的回调。因此,事件可能是一对多

可能不需要以这种方式处理命令。命令的发起者通常可以访问命令的预期执行者。例如,这可以是执行者的消息队列的形式。因此,命令旨在用于单个实体

答案 5 :(得分:1)

我觉得要添加到quentin-santin的答案中的一点是:

  

将请求封装为对象,从而允许您进行参数化   具有不同请求,队列或日志请求以及支持的客户端   可撤销的操作。

Source

答案 6 :(得分:1)

事件是过去的事实。

命令只是一个请求,因此可能会被拒绝。

  

命令的一个重要特征是它应该   由单个接收器处理一次。这是因为命令是   您要在应用程序中执行的单个操作或事务。   例如,不应处理同一订单创建命令   不止一次。这是命令和命令之间的重要区别   事件。事件可能会被多次处理,因为许多系统或   微服务可能对该事件感兴趣。 'msdn'

答案 7 :(得分:0)

您不能基于命令重新计算状态,因为通常它们在每次处理时都会产生不同的结果。

例如,假设有一个GenerateRandomNumber命令。每次调用它时,都会产生一个不同的随机数X。因此,如果您的状态取决于该数字,则每次您从命令历史记录中重新计算状态时,您都会得到一个不同的状态。

事件可以解决此问题。执行命令时,它会产生一系列事件,这些事件代表命令执行的结果。例如,GenerateRandomNumber命令可以产生一个GeneratedNumber(X)事件,该事件记录生成的随机数。现在,如果您从事件日志中重新计算状态,则将始终获得相同的状态,因为您将始终使用由特定命令执行生成的相同数字。

换句话说,命令是具有副作用的函数,事件记录特定命令执行的结果。

注意: 您仍然可以记录命令的历史记录,以进行审计或调试。关键是要重新计算状态,请使用事件的历史记录,而不是命令的历史记录。

答案 8 :(得分:0)

只需添加这些出色的答案即可。我想指出耦合方面的差异。

命令定向到特定处理器。因此,与命令启动器和处理器之间存在某种程度的依赖/耦合。

例如,UserService在创建新用户时会向EmailService发送“发送电子邮件”命令。

UserService知道它需要EmailService的事实,已经在耦合了。如果EmailService更改其API架构或出现故障,它将直接影响UserService函数。


事件不针对任何特定的事件处理程序。因此,事件发布者变得松散耦合。它不在乎什么服务消耗了它的事件。拥有0个事件使用者甚至是有效的。

例如,创建新用户时的UserService会发布“用户创建的事件”。 EmailService可能会消耗该事件并向用户发送电子邮件。

这里UserService不知道EmailService。它们是完全分离的。如果EmailService掉线或更改了业务规则,我们只需要编辑EmailService


两种方法都有其优点。纯粹的事件驱动架构设计很难跟踪,因为它耦合得太松散,尤其是在大型系统上。与Command重型架构的耦合程度很高。因此,理想的平衡是理想的。

希望如此。