CQRS和事件溯源在Java中的应用:不惧复杂性的架构

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

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

开始课程

引言:当CRUD力不从心时

\n\n

想象一下,你在编写一个股票交易系统。每秒都有成千上万的订单。每个订单不仅仅是“插入一条记录”。它涉及余额检查、投资组合重新计算、审计、通知。现在想象一下,你不仅需要记录当前状态,还需要恢复昨天14:35:22.003发生了什么。普通的CRUD(创建、读取、更新、删除)在这里行不通了。欢迎来到Java中的CQRS和事件溯源的世界。

\n\n

这两种模式不仅仅是时髦的词汇。它们是构建高性能、具备完整审计和“时间回溯”能力的系统的工具。在本文中,我们将探讨如何使用现代框架和简洁的代码在Java中实现CQRS(命令查询职责分离)和事件溯源。内容充实,附带示例。

\n\n

什么是CQRS?简单解释

\n\n

CQRS 是指命令(写入)和查询(读取)之间的职责分离。在经典的CRUD中,你使用同一个模型进行读写操作。而在CQRS中,你创建两个不同的模型:一个用于修改数据(命令),另一个用于检索数据(查询)。

\n\n

为什么需要它?

\n
    \n
  • 性能:读写可以独立扩展。你可以有10个读取副本和2个写入副本。
  • \n
  • 安全性:命令可以检查业务规则,而查询则只需返回数据。
  • \n
  • 灵活性:读取可以使用反规范化视图,写入可以使用规范化视图。
  • \n
\n\n

代码示例:命令与查询分离

\n\n

假设我们有一个订单管理系统。我们不使用 OrderService.save(order),而是进行分离:

\n\n
// 命令——改变状态的意图\npublic class CreateOrderCommand {\n    private final String orderId;\n    private final String customerId;\n    private final List<OrderItem> items;\n\n    // 构造函数、getter方法...\n}\n\n// 命令处理器\n@Component\npublic class CreateOrderCommandHandler {\n    @Autowired\n    private OrderRepository orderRepository;\n\n    public void handle(CreateOrderCommand command) {\n        // 业务规则验证\n        if (command.getItems().isEmpty()) {\n            throw new IllegalArgumentException("订单必须至少包含一个商品");\n        }\n        Order order = new Order(command.getOrderId(), command.getCustomerId(), command.getItems());\n        orderRepository.save(order);\n    }\n}\n\n// 查询——获取数据\npublic class GetOrderQuery {\n    private final String orderId;\n    // 构造函数、getter方法...\n}\n\n// 查询处理器\n@Component\npublic class GetOrderQueryHandler {\n    @Autowired\n    private OrderReadRepository orderReadRepository;\n\n    public OrderDTO handle(GetOrderQuery query) {\n        // 从优化的数据库中直接读取\n        return orderReadRepository.findById(query.getOrderId());\n    }\n}
\n\n

请注意:OrderRepository(写入)和 OrderReadRepository(读取)是不同的接口。它们可以指向不同的数据库或不同的模式。

\n\n

事件溯源:存储历史而非状态

\n\n

事件溯源是一种模式,我们不保存对象的当前状态,而是保存导致该状态的一系列事件。我们不存储“订单#123:状态 = 已送达”,而是存储:“事件1:订单已创建”,“事件2:商品已添加”,“事件3:订单已支付”,“事件4:订单已送达”。

\n\n

如何在Java中实现

\n\n

每个事件都是一个不可变对象。我们将它们保存在事件存储(一种专门的数据库)中。聚合的当前状态通过重放所有事件来计算。

\n\n
// 基础事件\npublic interface Do                

博客

书籍推荐