CQRS和事件溯源在Rust中:追求极致性能的架构

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

为什么CQRS和事件溯源不是“过度设计”,而是复杂系统的救星

\n\n

您是否曾遇到过这样的情况:用Rust编写的系统在处理读取请求时表现完美,但在写入时却“力不从心”?或者反过来——为了保存完整的历史变更记录而牺牲了读取性能?听起来很熟悉?那么欢迎来到CQRS和事件溯源的世界。

\n\n

许多开发者认为这些模式是“重型武器”,仅适用于C#或Java的企业级项目。但随着Rust在高负载系统中的流行(从游戏服务器到金融平台),CQRS和事件溯源不仅变得有用——它们变得必不可少。Rust凭借其零成本抽象和严格的类型系统,是实现这些模式的理想语言。

\n\n

在本文中,我们不仅会讨论理论。我们将编写可在Rust中运行的工作代码,您可以将其适配到自己的项目中。您将了解如何分离命令和查询的职责,如何存储事件而非当前状态,以及这一切如何成倍地加速您的系统。

\n\n

什么是CQRS和事件溯源?为Rust开发者用通俗语言解释

\n\n

想象一下您管理一个银行账户。在传统的CRUD模型中,您有一个“账户”表,其中存储当前余额。如果需要查看历史记录,您会再创建一个“交易”表。但如果您不仅需要知道余额,还需要理解为什么它发生了变化?或者需要恢复任意日期的状态?

\n\n

CQRS:分而治之

\n\n

CQRS(命令查询职责分离)是一种将操作分为两组的原理:

\n
    \n
  • 命令(Commands)——改变状态。它们不返回数据,只返回确认或错误。
  • \n
  • 查询(Queries)——只读取数据。它们不改变状态。
  • \n
\n\n

在Rust中,这种分离看起来很自然:命令和查询的类型是不同的结构体,它们的处理程序是不同的函数或模块。这不仅仅是“良好实践”,更是一种避免在读取时意外修改的方法。

\n\n
// 命令——改变状态\npub struct CreateOrder {\n    pub user_id: Uuid,\n    pub items: Vec<OrderItem>,\n}\n\n// 查询——只读取\npub struct GetOrderHistory {\n    pub user_id: Uuid,\n    pub limit: usize,\n}
\n\n

事件溯源:存储历史,而非快照

\n\n

事件溯源是一种方法,我们存储的不是对象的当前状态,而是导致该状态的一系列事件。想知道2023年1月1日的账户余额?只需从开始“重放”所有事件直到该日期。

\n\n

优势显而易见:

\n
    \n
  • 审计——每次变更都被记录。
  • \n
  • 时间切片——可以恢复任何状态。
  • \n
  • 对错误的鲁棒性——如果代码中有错误,您不会丢失数据,只是错误地“重放”了它们。
  • \n
\n\n

实践示例:基于Rust的订单管理系统

\n\n

让我们为在线商店构建一个小型系统。它将处理命令(创建订单、添加商品)和查询(获取购物车、订单历史)。所有逻辑都基于事件构建。

\n\n

步骤1:定义事件和命令

\n\n

首先要做的是设计领域。在Rust中,我们使用枚举(enum)表示事件,使用严格的结构体表示命令。

\n\n
use serde::{Deserialize, Serialize};\nuse uuid::Uuid;\n\n// 事件——不可变的事实\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub enum OrderEvent {\n    Created { order_id: Uuid, user_id: Uuid, timestamp: i64 },\n    ItemAdded { order_id: Uuid, item: Str                

博客

书籍推荐