CQRS 架构介绍
CQRS 本身是一种非常简单的模式。它仅描述处理命令的应用程序组件应与处理查询的组件分开。虽然这种分离本身非常简单,但与其他模式结合使用时,它提供了许多非常强大的功能。
下图显示了基于 CQRS 的事件驱动体系结构的扩展布局示例。左侧显示的 UI 组件以两种方式与应用程序的其余部分进行交互:向应用程序发送命令(如上半部分所示),以及查询应用程序以获取信息(如底部所示)。
命令处理
用户界面发送命令以对系统进行更改。命令是包含执行基础操作所需的所有数据的简单对象。该命令的一个示例可能是 MoveUserToNewAddress。该命令应保存用户的新地址和指示哪个用户已移动的用户 ID。
命令也通过名称来表达意图。例如,尽管命令 MoveUserToNewAddress 和 CorrectAddressForUser 都包含相同的数据 - 地址和用户 ID - 但意图肯定不同。
所有命令都发送到命令服务。此服务接收命令并将其路由到相应的命令处理程序。命令处理程序不应包含任何业务逻辑。他们唯一要做的就是对域中的聚合根进行更改并对其进行更改。
域
命令处理程序从存储库中检索域对象(聚合),并对其执行方法以更改其状态。所有业务逻辑都在这些对象中捕获,不用于查询。这使我们能够针对行为优化此模型。
聚合根包含实际的业务逻辑,并负责保护自己的不变量。聚合根上的状态更改会导致发生域事件。此域事件序列表示已进行的所有更改。此模式称为 事件溯源 。域事件和聚合都构成了域模型。
域事件
域中的所有状态更改都由域事件表示。它们是包含与更改相关的所有数据的简单对象。我们给出了两个命令名称的示例。与这些命令的状态更改相关的事件将是 UserMovedToNewAddress 和 AddressCorrectedForUser。请注意,名称是过去时。
存储库
存储库充当内存中的聚合集合。存储库允许我们通过聚合 id 获取单个聚合或将单个聚合根保存到其中。获取聚合根是通过获取其所有相关事件并重播它们以将聚合根构建到最新状态来完成的。保存聚合根将导致保留在对其进行更改时发生的所有未提交事件。事件存储在事件存储中,保存聚合根时,所有事件也将发布到事件侦听器。
事件存储
发生的所有事件最终都会在事件存储中。它包含表示系统中状态更改的所有事件。这些可用于通过重播它们来构建当前状态。此存储还可用于填充新的读取模型或修复现有的读取模型。
事件总线
通过存储库保存聚合根目录时,表示已进行的状态更改的所有未提交事件都将持久保存到事件存储中。除此之外,存储库还通过事件总线发布这些事件。此总线将其发布到事件侦听器,该侦听器已将自己注册为对事件感兴趣的事件侦听器。要做出的重要选择是总线是以同步还是异步方式将事件发布到订阅者。
同步意味着由订阅者完成的事件处理会阻止当前执行,并且这会等待所有订阅者完成,然后再返回给用户。此模型很简单,并且是明智的默认值。
异步不会等待所有订阅者完成,然后再返回给用户。它将在总线上丢弃事件并返回给用户。这将保持命令的快速执行,但引入 最终一致性 。读取的模型将在某个时间点保持一致。
事件处理程序
订阅了事件总线的不同事件处理程序。最常见的是去规范化器。这些事件处理程序获取事件,并基于这些事件对读取模型进行更改。例如,反规范化程序可以根据 UserMovedToNewAddress 事件中的数据更新用户表中的用户地址。但它也可以根据同一事件更新城市 X 的用户总数。
事件处理程序不仅使读取模型保持最新很有趣。但它们也可以被编写为对外部系统进行更改,或者在发生某些事件时向业务发送警告电子邮件。也可能是事件处理程序发出新命令。事件处理程序是很好的组件,可以使用新功能扩展系统,而无需对其进行更改。
读取模型
每个应用程序的一个重要部分是数据。用户界面中的大部分屏幕都会请求它。但是每个屏幕都只是为了对数据有不同的看法。例如,一个人想要所有具有名称,价格和类别的产品,而另一个想要具有名称和前 3 名最新产品评论分数和评论者姓名的产品。
读取模型是可以针对数据查询进行优化的模型。什么是最佳查询?这是一个仅查询来自一个源的数据的查询。换句话说:select * from x where y
。没有加入,只要给我数据。我们可以通过为每个视图创建一个表来实现这一点。这样每个视图都可以通过简单的查询来请求数据。
您的读取模型甚至不必是关系数据库,它可以是基于文档的数据库,其中包含每个视图的集合。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: Actor 的原理分析
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论