文章目录

  • 一 CQRS初探:理解基本概念
    • 1.1 什么是CQRS?
    • 1.2 CQRS与CRUD的对比
    • 1.3 为什么需要CQRS?
  • 二 CQRS深入:架构细节
    • 2.1 基本架构组成
    • 2.2 数据流示意图
  • 三 CQRS实战:电商订单案例
    • 3.1 传统CRUD方式的订单处理
    • 3.2 CQRS方式的订单系统
      • 3.2.1 命令端实现
      • 3.2.2 查询端实现
      • 3.2.3 读模型DTO
    • 3.3 数据同步实现
  • 四 CQRS进阶:高级话题
    • 4.1 最终一致性处理
    • 4.2 CQRS的适用场景
  • 五 CQRS实践建议
    • 5.1 实施步骤
    • 5.2 常见陷阱与解决方案
    • 5.3 性能考量
  • 六 总结

一 CQRS初探:理解基本概念

1.1 什么是CQRS?

  • Greg Young把增、删、改功能称为 Command(命令),把查询称为 Query,这两种功能的职责不同,应该采用不同的方式来处理,因此叫做“命令查询职责分离”(Command Query Responsibility Segregation ),简称 CQRS。
  • CQRS(Command Query Responsibility Segregation,命令查询职责分离)是一种架构模式,它的核心思想是将系统的**写操作(命令)读操作(查询)**分离,使用不同的模型来处理。
  • 想象一下图书馆的管理方式:借书和还书(写操作)由前台工作人员处理,而查询书籍位置或可用性(读操作)则由咨询台负责。这种职责分离提高了效率,这正是CQRS的核心思想。

1.2 CQRS与CRUD的对比

  • 传统CRUD(Create, Read, Update, Delete)架构中,读写操作使用同一个数据模型:
客户端
服务层
同一数据模型
数据库

而CQRS将读写分离:

客户端
命令模型
查询模型
写数据库
读数据库

1.3 为什么需要CQRS?

  • 读写负载不均衡:大多数系统读操作远多于写操作
  • 性能优化:可以为读写分别优化
  • 简化复杂性:避免单一模型同时满足读写需求带来的妥协
  • 扩展性:读写可以独立扩展

二 CQRS深入:架构细节

2.1 基本架构组成

一个典型的CQRS系统包含以下组件:

  1. 命令端(Command Side)

    • 处理创建、更新、删除等操作
    • 通过命令(Command)触发
    • 产生领域事件(Domain Events)
  2. 查询端(Query Side)

    • 处理数据查询
    • 针对展示需求优化
    • 通常是非规范化的数据视图
  3. 同步机制

    • 保持命令端和查询端数据一致性
    • 可通过事件溯源(Event Sourcing)或定期同步实现

查询端
同步机制
命令端
客户端
定期同步
查询处理器
DTO投影
事件处理器
读数据库
命令处理器
聚合根
领域事件
写数据库
命令模型
发送命令
查询模型
发起查询

2.2 数据流示意图

客户端 命令模型 写模型存储 读模型存储 查询模型 发送命令(如"下订单") 更新写模型 确认更新 返回结果 异步更新读模型 查询订单状态 获取数据 返回数据 返回查询结果 客户端 命令模型 写模型存储 读模型存储 查询模型

三 CQRS实战:电商订单案例

通过一个电商订单系统来具体理解CQRS的实现。

3.1 传统CRUD方式的订单处理

  • 在传统方式中,我们可能会有一个Order类同时处理读写:
public class Order {private Long id;private String customerId;private List<OrderItem> items;private OrderStatus status;private Date createdDate;// 读方法public BigDecimal calculateTotal() {return items.stream().map(i -> i.getPrice().multiply(i.getQuantity())).reduce(BigDecimal.ZERO, BigDecimal::add);}// 写方法public void addItem(Product product, int quantity) {// 验证逻辑...items.add(new OrderItem(product, quantity));}
}

这种方式随着业务复杂化会变得难以维护。

3.2 CQRS方式的订单系统

3.2.1 命令端实现

  • OrderCommandService.java (处理写操作)
public class OrderCommandService {private final OrderRepository orderRepository;private final EventPublisher eventPublisher;@Transactionalpublic void createOrder(CreateOrderCommand command) {// 验证业务规则if (command.getItems().isEmpty()) {throw new IllegalArgumentException("订单不能为空");}// 创建聚合根Order order = new Order(command.getOrderId(),command.getCustomerId(),command.getItems());// 保存orderRepository.save(order);// 发布事件eventPublisher.publish(new OrderCreatedEvent(order.getId(),order.getCustomerId(),order.getItems(),order.getStatus()));}public void cancelOrder(CancelOrderCommand command) {// 类似实现...}
}

3.2.2 查询端实现

  • OrderQueryService.java (处理读操作)
public class OrderQueryService {private final OrderReadRepository readRepository;public OrderDTO getOrderById(String orderId) {return readRepository.findById(orderId).orElseThrow(() -> new OrderNotFoundException(orderId));}public List<OrderSummaryDTO> getOrdersByCustomer(String customerId) {return readRepository.findByCustomerId(customerId);}
}

3.2.3 读模型DTO

public class OrderDTO {private String orderId;private String customerId;private List<OrderItemDTO> items;private String status;private BigDecimal totalAmount;private Date createdDate;// 仅包含简单getter/setter
}public class OrderSummaryDTO {private String orderId;private String status;private BigDecimal totalAmount;private Date createdDate;private int itemCount;// 仅包含简单getter/setter
}

3.3 数据同步实现

  • 使用领域事件同步读写模型:
@Component
public class OrderEventListener {private final OrderReadRepository readRepository;@EventListenerpublic void handleOrderCreated(OrderCreatedEvent event) {OrderDTO orderDTO = new OrderDTO();orderDTO.setOrderId(event.getOrderId());// 其他字段映射...readRepository.save(orderDTO);}@EventListenerpublic void handleOrderCancelled(OrderCancelledEvent event) {OrderDTO order = readRepository.findById(event.getOrderId()).get();order.setStatus("CANCELLED");readRepository.save(order);}
}

四 CQRS进阶:高级话题

4.1 最终一致性处理

由于读写分离,CQRS系统通常是最终一致性的。处理方式包括:

  1. 事件驱动的更新:通过领域事件触发读模型更新
  2. 补偿事务:当更新失败时执行补偿
  3. 版本控制:检测和处理并发冲突

4.2 CQRS的适用场景

CQRS并非银弹,适合以下场景:

  • 读写负载差异大的系统
  • 复杂领域模型,读写需求差异大
  • 需要高性能查询的系统
  • 需要审计日志或历史追踪的系统

不适合的场景:

  • 简单CRUD应用
  • 对实时一致性要求极高的系统
  • 开发资源有限的小型项目

五 CQRS实践建议

5.1 实施步骤

  1. 从简单开始:可以先在单个有界上下文(Bounded Context)中尝试
  2. 明确边界:清晰划分命令和查询的边界
  3. 渐进式演进:从分离模型开始,逐步引入事件溯源等高级特性

5.2 常见陷阱与解决方案

陷阱解决方案
过度设计从实际需求出发,只在必要时引入CQRS
数据不一致实现健壮的事件处理机制,监控延迟
事件风暴使用事件溯源时合理设计事件粒度
开发复杂性提供充分的文档和示例代码

5.3 性能考量

  1. 读模型优化

    • 使用非规范化设计
    • 针对查询场景定制数据结构
    • 考虑使用专门的查询数据库(如Elasticsearch)
  2. 写模型优化

    • 使用聚合根保证一致性边界
    • 合理设计命令处理流程
    • 考虑批处理和异步处理

六 总结

CQRS是一种强大的架构模式,通过分离读写职责可以带来诸多好处:

  1. 领域模型更清晰:命令端专注于业务规则,查询端专注于展示需求
  2. 性能更优:可以针对读写分别优化和扩展
  3. 灵活性更高:可以轻松添加新的查询而不影响命令处理

然而CQRS也带来了额外的复杂性,应该根据项目实际需求谨慎采用。对于初学者,建议从一个小的、非核心的功能开始实践,逐步积累经验。

  • 记住,架构模式是工具而非目标,选择适合你项目的最简单有效的方案才是明智之举。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/86746.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/86746.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/86746.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

项目测试-接口测试

软件测试的分类 软件测试主要分硬件和软件 硬件测试: cpu,内存条,显卡...测试可以看得见摸得着的东西 软件测试: web,app,小程序... 测试可以看得见摸不着的东西 web端 web端是在电脑上常常使用的, 也可以称之为网站.(web端是B/S架构) web端的客户端是任何一个访问这个网…

相机的光圈

光圈&#xff08;Aperture&#xff09;是镜头中一个控制光线进入相机的开口&#xff0c;它在摄影中起着至关重要的作用。光圈的大小决定了进入相机传感器的光线数量&#xff0c;并影响曝光、景深、以及拍摄效果。光圈参数通常用f/值&#xff08;光圈值&#xff09;来表示&#…

HarmonyOS NEXT仓颉开发语言实战案例:小而美的旅行App

大家周末好&#xff0c;本文分享一个小而美的旅行app首页&#xff0c;效果图如下&#xff1a; 很显然这个页面还是使用List容器&#xff0c;页面两侧有统一的边距&#xff0c;我们可以在List容器统一设置&#xff1a; List(space:20){ } .padding(left:14,right:14,top:62) .w…

Python银行管理系统01升级(适合初学者)

目录 框架如下: 1. Account类 - 账户数据模型 2. Bank类 - 银行业务逻辑 3. BankApp类 - 图形用户界面 关键概念解析(适合初学者) 1. 面向对象编程(OOP)概念 2. Tkinter GUI编程基础 3. 数据持久化 4. 输入验证 学习建议 系统功能概览 完整代码: 在Python银行…

华为防火墙双向NAT实验

如图所示&#xff0c; 企业内网有一台Server2&#xff0c;通过在FW1上配置nat server&#xff0c;将Server2的www端口映射到了公网&#xff1b; 实验环境中&#xff0c;内网和外网都使用外网的server1提供的DNS服务&#xff0c;在DNS服务器上添加A记录&#xff0c;www.baidu.c…

前端路由的基石:深度剖析 Hash 与 History 模式的本质差异与实战抉择

在单页面应用&#xff08;SPA&#xff09;统治现代Web开发的今天&#xff0c;前端路由已成为构建流畅用户体验的核心技术。而hash和history作为两种主流实现方案&#xff0c;其设计理念和技术细节的差异直接影响着应用架构的选择。本文将深入解析二者的技术本质&#xff0c;通过…

微机系统 - 绪论

绪论: 一:微处理器,微型计算机和微型计算机系统: 分类: 按照系统结构和基本工作原理.计算机分为5大部分:运算器,控制器,存储器,输入设备,输出设备 按照体积,性能和价格分5类:巨型机,大型机,中型机,小型机,微型计算机(单板机,单片机) 微型计算机的特点:集成度高,体积小,重量轻…

基于Java+Springboot的宠物健康咨询系统

源码编号&#xff1a;S564 源码名称&#xff1a;基于Springboot的宠物健康咨询系统 用户类型&#xff1a;多角色&#xff0c;用户、顾问、管理员 数据库表数量&#xff1a;12 张表 主要技术&#xff1a;Java、Vue、ElementUl 、SpringBoot、Maven 运行环境&#xff1a;Win…

SpringBoot+MySQL宠物猫店管理系统

概述 基于SpringBootMySQL开发的宠物猫店管理系统完整源码。该系统功能完善&#xff0c;包含前后台完整功能模块&#xff0c;代码规范易于二次开发&#xff0c;是学习SpringBoot项目实战的优秀范例。 主要内容 前台功能展示 系统前台设计简洁实用&#xff0c;主要包含以下核…

UE5 - 制作《塞尔达传说》中林克的技能 - 16 - 遥控炸弹(一)

让我们继续《塞尔达传说》中林克技能的制作&#xff01;&#xff01;&#xff01; 本章节的核心目标&#xff1a;素材导入与遥控炸弹的外观 先让我们看一下完成后的效果&#xff1a; 基本流程&#xff1a;素材准备->C类开发->蓝图配置->场景部署 1.素材准备&#xff1…

HTTP中常见的Content-Type

Content-Type&#xff0c;也称为互联网媒体类型或MIME类型&#xff0c;是HTTP协议中的一个头部字段&#xff0c;用于指定处理请求和响应中的媒体类型信息。它告诉服务器如何处理请求的数据&#xff0c;同时也指导客户端&#xff08;通常是浏览器&#xff09;如何解析响应的数据…

Android11 wifi开启源码分析

目录 一、APP层源码分析 1.1、寻找页面activity 1.2、寻找页面开关按钮布局 二&#xff0c;framework层代码分析 2.1 开启wifi入口 2.2 WiFiNative 三&#xff0c;HAL层代码分析 这段时间撸了WIFI开启流程源码&#xff0c;本着前人栽树后人乘凉的原则&#xff0c;有志于…

R语言使用nonrandom包进行倾向评分匹配

倾向评分匹配&#xff08;Propensity Score Matching&#xff0c;简称PSM&#xff09;是一种统计学方法&#xff0c;用于处理观察研究&#xff08;Observational Study&#xff09;的数据&#xff0c;在SCI文章中应用非常广泛。在观察研究中&#xff0c;由于种种原因&#xff0…

LeetCode Hot 100 找到字符串中所有字母异位词

给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 示例 1: 输入: s "cbaebabacd", p "abc" 输出: [0,6] 解释: 起始索引等于 0 的子串是 "cba", 它是 "a…

关于庐山派多视频层(layer)和bind_layer的应用

嘉立创分了适配层和OSD&#xff08;我称它为图片层&#xff09;顾名思义&#xff0c;一个是能显示视频流到LCD屏幕&#xff0c;一个是只能显示照片&#xff0c;也就是你可以对不同层进行操作而不影响其他层&#xff0c;解决的场景就是用于你画了一个正方形在照片上&#xff0c;…

多传感器标定简介

目录 标定内容及方法 雷达内参标定 IMU内参标定 编码器内参标定 相机内参标定 雷达和相机外参标定 多雷达外参标定 手眼标定 融合中标定 总结 连续时间 标定内容及方法 雷达内参标定 1) 目的 由于安装原因&#xff0c;线束之间的夹角和设计不一致&#xff0c;会导致…

day46/60

浙大疏锦行 DAY 46 通道注意力(SE注意力) 知识点回顾&#xff1a; 不同CNN层的特征图&#xff1a;不同通道的特征图什么是注意力&#xff1a;注意力家族&#xff0c;类似于动物园&#xff0c;都是不同的模块&#xff0c;好不好试了才知道。通道注意力&#xff1a;模型的定义和插…

提升创作效率:轻松调用固定素材与模板

日常工作和生活中&#xff0c;我们经常需要复制粘贴不同类型的数据&#xff0c;如文本、图片、文件等。使用剪切板管理工具可以快速访问之前复制的内容&#xff0c;而无需反复切换应用进行复制操作。 这款绿色便携版应用&#xff0c;无需安装&#xff0c;双击即开&#xff0c;…

【C++】组合模式

目录 一、模式核心概念与结构二、C 实现示例&#xff1a;文件系统三、组合模式的关键特性四、应用场景五、组合模式与其他设计模式的关系六、C 标准库中的组合模式应用七、优缺点分析八、实战案例&#xff1a;图形编辑器九、实现注意事项如果这篇文章对你有所帮助&#xff0c;渴…

C++包管理工具:conan2持续集成 (CI) 教程

1.持续集成 (CI) ​ 这是一个高级主题&#xff0c;需要具备 Conan 的基础知识。请先阅读并练习用户教程。本节面向设计和实施涉及 Conan 包的生产 CI 管道的 DevOps 和构建工程师。如果不是这种情况&#xff0c;您可以跳过本节。 持续集成 (CI) 对不同用户和组织有不同的含义…