认知篇:CQRS架构模式的本质
作者:京东科技 倪新明
CQRS只是一种非常简单的模式(pattern),CQRS本身并不是一种架构风格,和最终一致性/消息/读写分离/事件溯源/DDD等没有必然的联系,它最大优势是给我们带来更多的架构属性选择
1 CQRS 本质
1.1 CQS:命令和查询分离
命令和查询分离,Command and Query Segregation,其核心思想是在任何一个对象的方法可以划分为两类
基于CQS的思想,任何一个方法都可以拆分为命令和查询两部分:
private int origin = 0;
private int add(int value)
{
origin += value;
return origin;
}
上述方法既改变了数据,又返回了数据状态,如果按照CQS的思想,则该方法可以拆成Command和Query两部分,如下:
private void add(int value)
{
origin += value;
}
private int queryValue()
{
return origin;
}
是否严格遵循上述约定存在争议,对于命令侧是否返回数据实际业务诉求中并不一定能够完全统一。比如:
1.2 CQRS:命令和查询职责分离
Command and Query Responsibility Segregation,即命令查询职责分离,由Greg Young提出 。CQRS在CQS基础之上,将分离的级别从代码方法级别扩展到对象级别。CQRS 模式的应用非常简单,如下图所示

假设我们的服务为 OrderService,在非CQRS模式下同时包含了查询和更新服务接口:
public class OrderService {
// 根据id查询订单
Order getOrder(OrderId)
// 查询已支付订单
List<Order> getPayedOrders()
// 下单
void placeOrder(Order)
// 取消订单
void cancelOrder(OrderId)
}
应用CQRS模式之后的OrderService被拆分成了两个接口,分别承担查询和写职责:
/**
命令侧服务
*/
public class OrderService {
void placeOrder(PlaceOrderCommand command)
void cancelOrder(CancelOrderCommand command)
}
/**
查询服务
*/
public class OrderQueryService{
Order GetOrder(OrderId)
List<Order> getPayedOrders()
}
以上这种简单的分离就是CQRS模式的全部了,是不是非常简单?确实,单纯的看,CQRS的确就是这么简单。
CQRS最大优势就是基于这种职责分离能带给我们更多的架构属性选择。
基于CQRS,我们可以衍生出更多的架构属性,结合实际的业务场景,进行差异化的架构设计。
团队引入CQRS模式之后,往往不仅仅是简单的在类的职责层面对读写进行分离,一般会采用更为复杂的应用架构风格,如下是典型的CQRS架构风格:

2 CQRS迷思
2.1 数据模型是否要分离
CQRS强调命令和查询的职责分离,但在底层的数据模型层面,CQRS并没有进行强制限定,即采用CQRS模式并没有要求必须要进行数据模型的分离。是否要进行模型分离开发人员需要具体情况具体分析。
2.2 CQRS 和 消息模式
CQRS和消息模式没有必然联系,落地CQRS 并不一定需要使用消息模式。

如果我们采用了CQRS模式,但是命令和查询两侧底层所依赖的数据模型并未分离,而是基于共享的数据存储和数据模型,命令和查询之间不需要额外的交互,命令侧的数据更新对查询侧实时可见。在这种架构模式下,两侧基于共享的数据已经天然的集成在一起,不需要额外机制进行通信,自然也无需引入消息了。如果我们采用CQRS模式,并且命令和查询两侧进行了数据模型的分离,二者各自依赖独立的数据模型。同时,数据存储也分开部署。命令侧负责数据的更新,而查询侧只负责数据的查询,如何将数据的更新及时同步到查询侧是需要解决的问题。在这种架构模式下,使用消息模式作为两侧的通信机制是个不错的选择,当然,这并不是唯一的选项。
2.3 CQRS 和 ES(Event Sourcing, 事件溯源)
ES 并不是一个新的概念,在最早的金融系统中就已经应用。要了解ES,我们需要先看看传统的数据存储。在传统应用中,数据库例如MySQL(假设存储介质是数据库,)中存储的始终是数据的最新的状态。例如我们对某条用户的信息进行了多次的修改或编辑,然后保存将数据存储到数据库中。无论何时,数据库中都会记录最后的、最新的用户状态。我们只要根据id或其他信息查询数据库中相应的记录就能获取该用户的最新信息。这是应用中典型的数据存储特点。
当然,我们可以基于特定的数据模型设计以保存数据的更改记录。
这种数据存储模式的特点是简单,不需要额外的维护复杂的设计,我们能够非常容易的获取最新的用户信息。但是不幸的是,我们丢失了历史信息,包括用户的意图信息。而这些信息则有助于我们进行数据回滚、用户行为分析以及开发过程中的调试等等。

在ES模式下,数据库中存储的不在是数据最新状态,而是数据的变更记录,更官方的说法是 “事件(Event)”。数据库中存储的数据变化的事件流。我们基于事件流可以对最新状态进行重建,同时也可以便捷的重现任何历史节点数据。ES需要解决大量事件的存储和高效的实例重建问题,后续单独的文章再介绍ES。
2.4 CQRS 和 Eventual Consistency(最终一致性)
最终一致性也常常在服务之间引入,最终一致性的目的是为了提高扩展性和可用性。
CQRS和最终一致性同样没有必然的联系。往往采用CQRS后,查询和命令两侧会采用独立的数据模型,在这种架构模式下,命令侧的数据变化后及时同步到查询侧,两侧数据并非实时,在一定的延时后两侧数据最终达成一致。
3 结语
CQRS的最大优势在于通过将命令和查询的职责分离,为架构师提供了更多的架构属性选择,我们可以在查询侧和命令侧进行独立的架构设计。对象级别的职责分离就是CQRS的全部了,但在实践中涌现出了很多更为灵活也更为复杂的架构风格,比如总线的引入、数据模型的分离、一致性报这个策略、事件溯源等等。额外的组件或技术的引入必然导致复杂性和成本上升,这些选型的采纳需要团队的权衡。
认知篇:CQRS架构模式的本质的更多相关文章
- ASP.NET Core Web API下事件驱动型架构的实现(四):CQRS架构中聚合与聚合根的实现
在前面两篇文章中,我详细介绍了基本事件系统的实现,包括事件派发和订阅.通过事件处理器执行上下文来解决对象生命周期问题,以及一个基于RabbitMQ的事件总线的实现.接下来对于事件驱动型架构的讨论,就需 ...
- NET Core Web API下事件驱动型架构CQRS架构中聚合与聚合根的实现
NET Core Web API下事件驱动型架构在前面两篇文章中,我详细介绍了基本事件系统的实现,包括事件派发和订阅.通过事件处理器执行上下文来解决对象生命周期问题,以及一个基于RabbitMQ的事件 ...
- 转:命令和查询责任分离(CQRS)架构模式
读了“蓝皮书”距今差不多一年,它改变了我的软件开发和构建软件架构观.在我作为一名程序员期间,我尝试了许多不同的方式来构建软件.方法有很多,包括一个贫血的域模型(Anemic Domain Model) ...
- 架构模式: 命令查询职责分离 (CQRS)
架构模式: 命令查询职责分离 (CQRS) 问题 如何在微服务架构中实现查询 结论 将应用程序拆分为两部分:命令端和查询端.命令端处理创建,更新和删除请求,并在数据更改时发出事件.查询端通过对一个或多 ...
- 谈一下关于CQRS架构如何实现高性能
CQRS架构简介 前不久,看到博客园一位园友写了一篇文章,其中的观点是,要想高性能,需要尽量:避开网络开销(IO),避开海量数据,避开资源争夺.对于这3点,我觉得很有道理.所以也想谈一下,CQRS架构 ...
- CQRS架构如何实现高性能
CQRS架构如何实现高性能 CQRS架构简介 前不久,看到博客园一位园友写了一篇文章,其中的观点是,要想高性能,需要尽量:避开网络开销(IO),避开海量数据,避开资源争夺.对于这3点,我觉得很有道理. ...
- DDD CQRS架构和传统架构的优缺点比较
明天就是大年三十了,今天在家有空,想集中整理一下CQRS架构的特点以及相比传统架构的优缺点分析.先提前祝大家猴年新春快乐.万事如意.身体健康! 最近几年,在DDD的领域,我们经常会看到CQRS架构的概 ...
- Hibernate(1)——数据访问层的架构模式
俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及到的知识点总结如下: 数据库的概念.逻辑.数据模型概念 应用程序的分层体系结构发展 MVC设计模式与四层结构的对应关系 持久层的设 ...
- iOS 架构模式--解密 MVC,MVP,MVVM以及VIPER架构
本文由CocoaChina译者lynulzy(社区ID)翻译 作者:Bohdan Orlov 原文:iOS Architecture Patterns 在 iOS 中使用 MVC 架构感觉很奇怪? 迁 ...
- Hibernate(1)——数据访问层的架构模式<转>
数据库的概念.逻辑.数据模型概念 应用程序的分层体系结构发展 MVC设计模式与四层结构的对应关系 持久层的设计目标 数据映射器架构模式 JDBC的缺点 Hibernate简介 迅速使用Hibernat ...
随机推荐
- HTML5+CSS3常见布局方式
1.等高布局 1.1 代码 等高布局是指子元素在父元素中高度相等的布局方式 <div class="father"> <div class="f1&qu ...
- 1分钟完成在线测试部署便捷收集班级同学文件的web管理系统
最近CSDN推出了一个新功能[云IDE],个人对这个新功能(比赛奖金 )挺感兴趣的,于是瞬速地拿之前自己搞的一个便捷收集班级同学文件的web管理系统(下面简称该项目为cfile)体验了一下,发现功能还 ...
- mysql网上知识
MySQL学习笔记 登录和退出MySQL服务器 # 登录MySQL $ mysql -u root -p12345612 # 退出MySQL数据库服务器 exit; 基本语法 -- 显示所有数据库 s ...
- Redis Cluster 数据分片
介绍 Redis Cluster Redis 集群是 Redis 提供的分布式数据库方案, 集群通过分片(sharding) 来进行数据共享, 并提供复制和故障转移功能. 节点 一个 Redis 集群 ...
- Spring Security(1)
您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来- 虽然说互联网是一个非常开发.几乎没有边界的信息大海,但说起来有点奇怪的是,每个稍微有点规模的互联网应用都有自己的权限系统,而权限的本质却是是封闭 ...
- 【开发必备】单点登录,清除了cookie,页面还保持登录状态?
背景 本地搭建了一台认证服务器.两台资源服务器,看看请求的过程 开始 没登录,直接请求资源服务器,结果跳转到的登录页面 登录后,请求了认证服务器的登录接口,然后顿重定向,最后回到了资源服务器的接口,页 ...
- 【Java Web】项目通用返回模块ServerResponse:枚举code状态码、泛型返回值、序列化注解限制数据
一.枚举类编写ResponseCode package com.boulderaitech.common; /** * 编写枚举类的步骤 * (1)编写所需的变量 * (2)编写枚举类构造方法 * ( ...
- 【Java SE】Day04 IDEA、方法*
一.IDEA 1.快捷键 Ctrl+Alt+L/Ctrl+Alt+Shift+4:格式化代码 Alt+Insert:自动生成代码 修改快捷键:File->Settings->keymap- ...
- 【论文解读】NIPS 2021-HSWA: Hierarchical Semantic-Visual Adaption for Zero-Shot Learning.(基于层次适应的零样本学习)
作者:陈使明 华中科技大学
- TS编写发布订阅模式
interface PubSubType { events: { [key: string]: { name: string, once: boolean, cb: Function }[] } on ...