强调事件的架构中的命令和事件有什么区别?我能看到的唯一区别是命令通常是由系统外部的参与者获取/调用的,而事件似乎是由系统中的处理程序和其他代码获取的。但是,在我见过的许多示例应用程序中,它们具有不同(但功能相似)的接口。
9 回答
Commands can be rejected.
Events have happened.
This is probably the most important reason. In an event-driven architecture, there can be no question that an event raised represents something that has happened.
Now, because Commands are something we want to happen, and Events are something that has happened, we should be using different verbs when we name these things. This drives separate representations.
I can see is that commands are usually sourced/invoked by actors outside the system, whereas events seem to be sourced by handlers and other code in a system
This is another reason they are represented separately. Conceptual clarity.
Commands and Events are both Messages. But they are in fact separate concepts, and concepts should be modeled explicitly.
该事件是过去的事实。
该命令只是一个请求,因此可能会被拒绝。
命令的一个重要特征是它应该由单个接收器只处理一次。这是因为命令是您要在应用程序中执行的单个操作或事务。例如,不应多次处理同一订单创建命令。这是命令和事件之间的重要区别。事件可能会被处理多次,因为许多系统或微服务可能对事件感兴趣。'msdn'
此外,除了此处公开的所有答案之外,事件处理程序还可以在收到事件发生的通知后触发命令。
例如,在您创建客户之后,您还想初始化一些帐户值等。在您的客户 AR 将事件添加到 EventDispatcher 并且由 CustomerCreatedEventHandler 对象接收之后,此处理程序可以触发命令的调度将执行你需要的任何东西,等等。
此外,还有 DomainEvents 和 ApplicationEvents。区别只是概念上的。您想先调度所有域事件(其中一些可能会产生应用程序事件)。我这是什么意思?
在发生 CustomerCreatedEvent 后初始化帐户是 DOMAIN 事件。向客户发送电子邮件通知是一个应用程序事件。
你不应该混合它们的原因很清楚。如果您的 SMTP 服务器暂时关闭,这并不意味着您的 DOMAIN OPERATION 会受到影响。您仍然希望保持聚合的未损坏状态。
我通常将事件添加到聚合根级别的调度程序。此事件是 DomainEvents 或 ApplicationEvents。可以是两者,也可以是其中的许多。一旦我的命令处理程序完成并且我回到堆栈中执行命令处理程序的代码,然后我检查我的调度程序并调度任何其他域事件。如果这一切都成功,那么我关闭交易。
如果我有任何应用程序事件,现在是分派它们的时候了。发送电子邮件不一定需要打开与数据库的连接,也不一定需要打开事务范围。
我偏离了最初的问题,但了解事件在概念上的不同处理方式也很重要。
然后你有 Sagas.... 但这是这个问题范围的 WAYYYY OFF :)
是否有意义?
在完成了一些示例,尤其是 Greg Young 的演示文稿 ( http://www.youtube.com/watch?v=JHGkaShoyNs ) 之后,我得出的结论是命令是多余的。它们只是来自您的用户的事件,他们确实按下了该按钮。您应该以与其他事件完全相同的方式存储它们,因为它是数据并且您不知道是否要在将来的视图中使用它。您的用户确实添加了该项目,然后从篮子中删除了该项目,或者至少尝试这样做。您稍后可能希望使用此信息来提醒用户这一点。
它们被分开表示,因为它们代表了非常不同的事物。正如@qstarin 所说,命令是可以被拒绝的消息,成功时会产生一个事件。命令和事件是 Dto,它们是消息,它们在创建和实体时往往看起来非常相似,但从那时起,就不一定了。
如果您担心重用,那么您可以将命令和事件用作(消息)有效负载的信封
class CreateSomethingCommand
{
public int CommandId {get; set;}
public SomethingEnvelope {get; set;}
}
但是,我想知道的是您为什么要问 :D 即您是否有太多命令/事件?
除了上面提到的概念差异之外,我认为还有一个与常见实现相关的差异:
事件通常在需要轮询事件队列的后台循环中处理。任何有兴趣对事件采取行动的一方通常都可以注册一个回调,该回调会作为事件队列处理的结果而被调用。所以一个事件可能是一对多的。
命令可能不需要以这种方式处理。命令的发起者通常可以访问命令的预期执行者。例如,这可以是执行者的消息队列的形式。因此,命令是针对单个实体的。
只是为了添加这些很棒的答案。我想指出耦合方面的差异。
命令指向特定的处理器。因此,命令发起者和处理器存在某种程度的依赖/耦合。
例如,UserService
在创建新用户时向EmailService
.
UserService
知道它需要 的事实EmailService
已经是耦合的。如果EmailService
更改其 API 架构或出现故障,则会直接影响UserService
功能。
事件不针对任何特定的事件处理程序。因此,事件发布者变得松散耦合。它不关心什么服务使用它的事件。拥有 0 个 Event 消费者甚至是有效的。
例如,UserService
在创建新用户时发布“用户创建事件”。可能EmailService
会消耗该事件并向用户发送电子邮件。
这里UserService
是不知道的EmailService
。它们是完全解耦的。如果EmailService
出现故障或更改业务规则,我们只需要编辑EmailService
这两种方法都有优点。纯粹的事件驱动架构设计更难跟踪,因为它耦合太松散,尤其是在大型系统上。并且命令重架构具有高度耦合。因此,良好的平衡是理想的。
希望这是有道理的。
您无法根据命令重新计算状态,因为通常它们每次处理时都会产生不同的结果。
例如,想象一个GenerateRandomNumber
命令。每次调用它都会产生一个不同的随机数 X。因此,如果你的状态取决于这个数字,那么每次你从命令历史中重新计算你的状态时,你都会得到一个不同的状态。
事件解决了这个问题。当您执行命令时,它会产生一系列事件,这些事件代表命令执行的结果。例如,该GenerateRandomNumber
命令可以生成一个GeneratedNumber(X)
记录生成的随机数的事件。现在,如果您从事件日志中重新计算您的状态,您将始终获得相同的状态,因为您将始终使用由特定命令执行生成的相同数字。
换句话说,命令是具有副作用的函数,事件记录了命令特定执行的结果。
注意:您仍然可以记录命令历史记录以进行审计或调试。关键是要重新计算状态,您使用事件的历史记录,而不是命令的历史记录。