命令和事件有什么区别? - Oskar


命令代表意图:它针对特定的受众。当你问“把盐递给我”时,它可以是你的朋友。它可以是一个应用服务和请求,意图是“添加用户”或“将订单状态更改为已确认”。所以命令的发送者必须知道接收者并期望请求被执行。当然,接收者可能会拒绝这样做,因为在请求处理期间不向我们传递盐或抛出异常。
事件代表了过去的事实:它携带有关已完成某事的信息。所见所闻,不可不察。按照我们的例子“用户添加”,“订单状态更改为确认”是过去的事实。我们不会将事件定向到特定的接收者,我们会广播信息。这就像在聚会上讲故事一样。我们希望有人倾听我们,但我们可能很快就会意识到没有人在关注。
 
事件和命令有什么共同点?
两者都是消息。它们传达特定的信息;关于做某事的意图的命令,或关于发生的事实的事件。从计算机的角度来看,它们没有什么不同。只有业务逻辑和消息的解释才能发现事件和命令的区别。
通常假设命令是同步的,而事件是异步的。我们通常通过例如 Rest API 发送命令,通过队列(内存中、RabbitMQ、Kafka 等)发送事件。这种区别来自习惯。当我们发送命令时,我们想立即知道它是否已经完成。通常,我们希望在操作失败时进行替代方案或错误处理。同样,我们通常假设最好立即停止该过程,例如购买电影票,而不是等待并刷新并查看它是否有效。
这是有道理的,但并不总是那么明显。例如,银行转账:当我们成功时,它不会立即发生。我们得等一会儿。在 Internet 上进行购买时也是如此。下订单和付款不会立即完成整个过程。它还是要发货,发票开具等等。这是一个异步过程,所以我们的命令的结果也可能是异步的。
 
微服务和分布式系统增加了额外的复杂性。
传统上,发件人需要知道该告诉谁来接管其余的工作。使用排队/流系统逆转服务的依赖性。发送者将消息发布到统一频道,接收者订阅并等待。当我们购买电影票时,必须生成收据,必须预订座位,并通过电子邮件通知用户并在网站上显示。所有这些都可以拆分为单独的工作流程步骤。哪个应该由命令触发,哪个应该由事件触发?
我们可以使用启发式:当接收者有权拒绝请求时,我们发送命令,当接收者接受时发送事件。如果我们要添加用户,系统可能会拒绝我们使用现有用户名。金融服务可以拒绝预订服务生成收据吗?如果预订被确认,转账已经完成,那么财务模块应该接受它。那么,这是一个事件吗?
理论上,是的,如果没有验证规则并且事件发生了,另一个系统应该接受它并执行逻辑。即使金融服务暂时不可用,类似 Kafka 的流媒体系统也应保证交付。我们在代码中有错误?然后我们必须修复它们。
到这里,理论结束,实践开始。我们的客户不在乎错误是否是我们知道并在不久的将来修复的错误?客户想要开展业务并运营。
当然,在消息队列教程中,你可以找到这样的建议:“有一个死信队列/毒消息队列,未处理的消息将被放置在那里。你可以在那里检查并做出反应”。这很酷,但有多少人在监视它?即使他们这样做了,他们能多快做出反应?假设我们收集指标、发送警报、快速支持响应并向程序员报告“票”。他们会多快修复它?我们可以通过预先设计并添加补偿操作(例如,如果预订确认事件丢失时在 UI 中生成发票的按钮)来简化这一点。
有时事实证明,一个给定的事件总是只有一个接收者。更重要的是,我们希望它始终得到处理。它仍然是一个事件吗?值得考虑的是,在名称为“预订已确认”的事件下,我们是否不发送“发出收据”命令。当业务流程至关重要时,我们可能更愿意快速失败并立即获得结果。对于这种情况,值得考虑是否执行异步命令比执行异步事件更好。
 
命令和事件之间的区别可能并不像看起来那么简单。我们需要考虑很多因素:

  • 是有意做某事还是记录的事实?
  • 是异步操作还是同步操作?
  • 它可以有多个收件人吗?
  • 它是关键业务吗?
  • 还有很多

如果有人问你“这是一个命令还是一个事件”,不要害怕说“这取决于”,然后开始着手理解业务逻辑。