DDD中事件与命令比较

在探索领域驱动设计主题时,事件和命令之间的差异以及何时应该使用哪些事件和命令?

事件
事件 – 当我们想要传达某事已发生并且我们并不真正关心谁将收到此通知以及他们将如何处理它时,我们会发送它。许多订阅者可以监听一个事件——一对多的关系。时间是这里的一个重要方面 - 事件在应用操作后发送 - 它讲述已经发生的事情。

命令
命令 - 这是一个更包装的请求,我们发送到特定的(理论上只是技术)服务,以便该服务执行给定的操作 - 一对一的关系。在这种情况下,就时间而言,我们处于执行操作之前。如果发送命令时出现错误 - 尚未发生任何事情,我们可以重复尝试发送命令。

看起来很简单,对吧?

让我们通过示例来检查一下
不幸的是,想出 DDD 的例子总是很困难。如果我们从真实系统中获取这些数据,那么在不了解整个领域的情况下分析它们将会更加困难。因此,这里仍然使用老掉牙的电子商务或银行。

用户操作顺序:

  • 创造订单
  • 将产品添加到订单中
  • 订单的税费计算
  • 确认顺序
  • 订单支付

在命令的情况下,一切都是同步执行的。用户创建订单,将产品添加到订单中,确认订单,然后付款。通信通过队列进行,给定组件立即收到响应。

对于事件,我们使用具有单独主题的异步通信来进行响应。

在这里,在输入通信 PaymentRequested - Event 和 Tax Calculation Requested - Event 的情况下是否无法用命令替换,我们只会接收事件作为响应。

在这种情况下,我们使用事件还是命令实际上并不重要。直到只有一个组件在监听!

现实世界的例子
牛仔订购啤酒,用于比较产品订购模型中的事件与命令

  • [酒吧顾客 -> 女服务员]请来两杯啤酒[命令]
  • [女服务员->调酒师]顾客点了两瓶啤酒[事件]
  • [调酒师 -> 女服务员] 2 瓶啤酒已准备好 [事件]

或者:
  • [调酒师 -> 女服务员](通过)给这位先生 2 瓶啤酒 [命令]
  • [服务员->顾客] 2瓶啤酒已经准备好了,请拿走[事件]

或者:
  • [女服务员 -> 顾客] 这 2 瓶啤酒是给您的,请 [命令]

事件、命令两者关系是什么?

  • 命令触发网站上的操作,然后发送一个事件来传达操作的应用程序。
  • 另一个服务响应此发送的事件,执行该事件的内部命令以更改服务中的状态,并为下一个服务生成另一个事件。

传输层
我们将研究与事件和命令相关的服务之间的通信层。对于命令,我们最常使用队列(Queue),对于事件,我们应该使用发布/订阅(Topic)。

当涉及到要使用的特定代理时,大多数都支持队列和主题这两种方法:

  • IBM MQ
  • Apache Kafka
  • RabbitMQ
  • ActiveMQ
  • Amazon SQS (Simple Queue Service)
  • Google Cloud Pub/Sub

事件/命令的结构是什么?
事件和命令之间的另一个重要区别(乍一看并不明显)是结构的责任。

  • 如果是事件,发布者将决定事件的形式。如果根据定义,他对是否有人会阅读该事件以及如果是的话,有多少个实体不感兴趣,那么基于同样的基础,他是结构的所有者,并决定给定事件应该是什么样子。
  • 命令的情况并非如此。在这里,两个服务之间存在直接通信,并且由接收者决定他可以接受什么以及他将处理的命令应该是什么样子。

事件与命令相关的异步/同步
在发现我们在队列中使用命令和在主题中使用事件之后,我们可以指出另一个区别。

默认情况下,每个命令都是同步的,但每个事件都是异步的。

尽管实际上没有什么可以阻止您通过队列以异步方式发送命令并以异步方式等待返回队列上的响应。例如,如果我们预计处理这样的命令将花费很长时间。