GMTC全球大前端技术大会(北京站)门票9折特惠截至本周五,点击立减¥480 了解详情
写点什么

详解 CQRS 架构模式

2021 年 4 月 25 日

详解CQRS架构模式

从一开始,软件系统就被用于各种用途,针对它们的需求也随着时间的推移而增长。需求的变更可能与业务逻辑、伸缩性或系统的其他方面有关。


为了满足这些相互矛盾或重叠的需求,工程师必须在设计系统时做出各种各样的权衡。问题在于,很多权衡在一开始并不是必需的,而当需要做出权衡时,系统已经演变成到无法做出权衡的地步。


在我看来,最有害的设计锁定通常发生在数据层。在设计典型的应用程序数据模型时,通常会结合考虑领域知识与性能因素。领域知识规定了实体是什么以及它们在逻辑上如何相互关联,性能因素决定了它们是如何在物理层面实现的(例如:采用关系型数据库还是 NoSQL 数据库、主键、索引等)。这两个方面的选型让应用程序能有效地为目标场景提供服务。



数据及其不同的视图


在拥有大量数据和复杂实体模型的大型应用程序中,一些实现细节随着时间推移变成了“核心”部分。有时候,这些东西是工程师在很明确的情况下完成的,但更多的是以一种隐式甚至是无意的方式发生。于是,新需求可能与现有的实现不一致,以至于根本无法很好地容纳它们。


这类问题在不同的情况下需要不同的解决方案。在本文中,我将重点关注一种情况,即从应用程序读取数据的方式与向系统写入数据的方式非常不同时所出现的问题。这里的不同点可以是指查询模式、输出格式或规模方面的不同。


我在这篇文章里写了自己所遇到的这种情况。我当时正在开发的订单管理系统使用了实体 ID (订单 ID、商品 ID 等),但是随着时间推移,出现了一些复杂的读取需求,我们的数据模型无法支持这些需求。问题出在两个方面:


一方面,现有的实现很难有效地满足新的查询模式。另一方面,订单数据的读取方希望有一种截然不同的数据模型。例如,电子商务平台上的卖家希望他们的大客户数据切片能以特定的方式来呈现,而面向客户的应用程序希望数据看起来与购物车中的样子一样。


这种情况并不少见,特别是对于拥有核心实体的系统。它们封装的数据被广泛使用,因此需要提供多种不同的格式。


那么,我们该如何弥合这一鸿沟?

CQRS

CQRS 是“命令查询责任分离”(Command Query Responsibility Segregation)的缩写。在基于 CQRS 的系统中,命令(写操作)和查询(读操作)所使用的数据模型是有区别的。命令模型用于有效地执行写/更新操作,而查询模型用于有效地支持各种读模式。通过领域事件或其他各种机制将命令模型中的变更传播到查询模型中,让两个模型之间的数据保持同步。



如果你觉得它们看起来就像是两个不同的微服务,那么我来说一说它们之间的一个细微区别。从物理实现层面来看,这两个数据模型可以作为两个独立的微服务,甚至可以用一个命令模型来支持多个查询模型。但是,微服务架构的一个关键构造是两个微服务通常代表两个独立的领域,而在 CQRS 中,无论运行时架构是怎样的,命令模型和查询模型都属于同一逻辑领域。如果查询模型对命令模型一无所知,就无法发挥作用。这里的耦合是预期的,不同于微服务之间的解耦行为。


CQRS 并没有规定这两个模型如何保持同步。同步可以通过同时更新两个模型来同步实现,也可以通过消息代理(如 Kafka)将命令从命令模型传输到查询模型来异步实现。后一种比较常用,因为它让系统更加可伸缩,尽管它需要在写操作和读操作的最终一致性方面做出权衡。



这不就是缓存吗?

只用于读取的数据模式看起来就像是一个缓存。事实上,查询模型可以使用 Redis 这样的缓存技术来实现。但是,CQRS 不只是为了分离数据的写入和读取,它的根本目的是为了实现数据的多重表示,每一种表示都能够满足某些用户的需求。CQRS 可能会有多种查询模式,每个模式可能使用不同的物理实现。有些可能使用数据库,有些可能使用 Redis,等等。

什么时候应该使用 CQRS

对于一部分场景,CQRS 是一种非常有用的架构模式。


第一个是我在前面已经提到过的。如果同一个数据模型不能有效地满足系统的读和写模式,那么通过应用 CQRS 来解耦读写是很有意义的。解耦后的数据模型可以满足特定的需求。CQRS 有效地将单个数据表示变成任意数量的(读)表示,所有这些表示都与负责处理所有更新的核心表示保持一致。


适用 CQRS 的第二个场景是将读负载与写负载分开。前面我讲了缓存和 CQRS 的区别,缓存并不是应用 CQRS 的目的。但是,通过分离命令模式和查询模式,就有了对单个模式进行伸缩的可能性。查询模型可以有自己的数据库和缓存,可以使用最适合某些特定场景的技术来实现。但不管怎样,命令模型的伸缩都不会受制于查询模型。我在这里需要重申的是,它们不是独立的系统,尽管它们之间有深度的耦合,但这不是问题。

什么时候不该使用 CQRS

在系统中使用 CQRS 会带来显著的认知负担和复杂性。开发人员必须面对至少两个数据模型和多种技术选择,所有这些都是不可忽略的负担。


第二个问题是如何保持命令模型和查询模型的数据同步。如果选择了异步方式,那么整个系统就要承担最终一致性所带来的后果。这可能非常麻烦,特别是当用户希望系统能够立即反映出他们的操作时,即使是单个一致性要求也会危及整个系统的设计。


如果我们选择让模型在任何时候都保持一致,就会有 CAP 和两阶段提交问题。如果两个模型使用同一个支持 ACID 的数据库,我们可以通过事务来保持它们的一致性,但 CQRS 的很多可伸缩性优势就发挥不出来了。如果要支持多个查询模型,写操作将会越来越慢,因为需要更新所有的查询模型。


因为这两个问题的存在,在选择是否使用 CQRS 时就要十分谨慎。如果使用得当,它可以极大提升应用程序的伸缩性。但是,支持多个数据模型并不是件容易的事,所以应该只在没有其他方法可以满足要求时才考虑这么做。


原文链接:


https://kislayverma.com/software-architecture/architecture-pattern-cqrs/?fileGuid=0IWvR8dLbi0m7fi4

2021 年 4 月 25 日 11:352067
用户头像

发布了 114 篇内容, 共 25.0 次阅读, 收获喜欢 286 次。

关注

评论

发布
暂无评论
  • DDD 分层架构:有效降低层与层之间的依赖

    DDD分层架构的出现使架构边界变得越来越清晰,它在微服务架构模型中,占有非常重要的位置。

    2019 年 10 月 28 日

  • 面向企业级 Web 开发的 CQRS:它能为业务带来什么价值?

    本文主要关注CQRS架构的业务场景,涵盖了命令查询职责分离(Command Query Responsibility Segregation)的核心理念,并与通用的N层架构进行了对比。它所带来的收益主要在于可扩展性和可维护性,如果选择CQRS架构的话,能够减少总拥有成本,并且会带来投资回报率的增长。

  • 比较 NHibernate 和 Entity Framework

    葡萄牙的一位开发者Ricardo Peres最近发布了一篇文章,以看起来无偏见的形式对领先的两种.NET ORM: NHibernate和Entity Framework进行了比较。 我们建议考虑使用这两种框架的人都应该读下他的文章《NHibernate和Entity Framework之间的区别》,另外还将指出二者之间一些关键的区别。

  • MVC 架构解析:模型(Model)篇

    模型就是当我们使用软件去解决真实世界中各种实际问题的时候,对那些我们关心的实际事物的抽象和简化。

    2019 年 9 月 27 日

  • 技术架构:作为开发,你真的了解系统吗?

    系统比我们想象的要复杂得多。要深入掌握技术架构,我们就需要了解整体的系统。

    2020 年 3 月 16 日

  • 从 CQS 到 CQRS

    作者介绍了CQS到CQRS的演化过程,并举例解释了CQRS的优缺点。

  • Datomic 的架构

    Rich Hickey是Clojure的作者,他说明了Datomic的架构 —— 一种以简单服务组合为设计目标的新数据库,它结合了RDBMS的功能和NoSQL的可扩展性。

  • 聊聊微服务的通信模式

    微服务的目标是将应用程序尽量分解 / 解耦为围绕业务功能组织的一系列松散耦合服务。这些分布式的微型单元共同满足应用程序的目标。

  • Stefan Tilkov:跳过单体应用,从微服务开始

    在过去的几个月中,许多人都宣称微服务架构应该总是从单体应用开始,其中包括Martin Fowler和Sam Newman,但Stefan Tilkov认为,那经常是错误的,构建一个模块边界清楚、结构良好的单体应用然后再迁移到微服务在大多数情况下都非常困难,几乎不可能。

  • CQRS 的益处

    Gabriel Schenker表示,由于没有使用命令与查询职责分离(CQRS)架构,因此当今的应用程序普遍地体现出不必要的复杂性或低效性,他相信,在基于复杂的业务线应用程序的上下文中,CQRS将成为最具实用性的架构模式之一。

  • 使用 Axon 框架实现一个 CQRS 示例应用

    命令查询职责分离(CQRS)的思想是将对状态的查询部分与改变状态的部分进行分离。Axon框架是一个基于Java实现的CQRS框架,提供了对大多数重要构建块的实现,以帮助开发者在构建应用程序时使用CQRS架构模式。Dadepo Aderemi最近撰写了一系列博客文章,通过一个小型的CQRS演示应用讲解了CQRS的概念。

  • 不可靠世界的事件溯源

    Lorenzo Nicora在最近的µCon London2017微服务讨论会上说明道:事件溯源系统的例子通常来自像电子商务这样的领域,这些领域生成事件的指令输入是面向过程的,同时这样的例子也常见于我们能够控制过程的地方。但有些领域是没有过程的,属于我们搜集外部事件的领域。这些领域的事件源本身就不可靠,传输也不可靠。

  • 领域事件与最终一致性

    最终一致性是一种改进规模和性能的设计方法。领域事件是领域驱动设计(DDD)中的一个战术元素,能够帮助促进最终一致性,Florin Preda和Mike Mogosanu在各自的博客文章中描述了一些可实现的用处。

  • 总结(二):分布式架构关键设计 10 问

    中台大多基于分布式微服务架构,这种企业级的数字化转型有很多地方值得我们关注和思考。

    2019 年 11 月 29 日

  • Stefan Tilkov 在 microXchg 柏林的演讲:微服务的模式与反模式

    在柏林举行的microXchg 2018上,Stefan Tilkov在演讲中以他的视角探讨了微服务项目的模式与反模式,包括演化性的架构、基于幻想进行解耦、分布式单体以及实体服务。他特别强调,有些他认为是模式的内容,其他人可能认为是反模式,反之亦然。

  • 聊聊微服务架构中的事务处理

    当从一个单体系统转向微服务架构时,处理分布式系统带来的复杂性是一个挑战。事务处理是其中的首要核心问题。在一个 Web 应用程序中使用本地事务完成的典型数据库事务,现在是一个复杂的分布式事务问题。在本文中,我们将讨论造成这种情况的原因、可能的解决方案以及使用 MSA 开发安全事务性软件系统的最佳实践。

  • 产品 0 期 - 第一周作业

    第1周作业-岗位模型

    2021 年 1 月 19 日

  • InfluxDB 企业版一致性实现剖析:他山之石,可以攻玉

    InfluxDB企业版一年的License费高达1.5万美刀,为什么它值这个价钱?就是因为技术带来的高性能和成本优势。

    2020 年 3 月 18 日

  • 数据存储:物联网中的数据库有哪些?

    跟灵活多变的数据处理框架比起来,数据存储方案要固定得多,一旦确定软件就很难切换。所以我们做选型的时候,必须非常谨慎。

    2020 年 12 月 2 日

  • Bob 大叔曰:架构在于目的而非框架

    “架构的核心是目的,而我们却把它变成了框架和细节”,Robert C. Martin(又名“Bob大叔”)在今年伦敦举办的DDD Exchange Day大会上说道。为改进这些架构模型,Robert引用了由Ivar Jacobson在1992年出版的一本书,并将有关用例的最初思考引入到架构模型中,例如Hexagonal架构和Clean架构。

发现更多内容

新业务团队应用数字化的4个能力

boshi

数字化转型 七日更

在有限的时间里,拿到通才的帐号,登入无限的游戏。

叶小鍵

文字君和ta的朋友们

InfoQ写作平台官方

打造移动版的开发环境

雨夜的博客

php vagrant 移动版开发环境

加快推进数据确权与资产化 发挥区块链技术信用机制优势

CECBC区块链专委会

区块链

虚拟化存储

lenka

产品经理 3月日更

大数据热是华而不实吗?大数据和小数据有什么本质区别

读字节

大数据 物联网 数据隐私 大数据 Google 小数据

翻译:《实用的Python编程》04_00_Overview

codists

Python

常用工具幕布高级会员获取

白程序员的自习室

工作中迷迷糊糊,不知道自己想要什么?

一笑

28天写作

面试官就是这么欺负人:new Object()到底占用几个字节?

xcbeyond

Java java对象分析 3月日更

面向业务的高可用架构设计

架构精进之路

架构设计 七日更 3月日更

如何设计三极管控制继电器电路

不脱发的程序猿

28天写作 电路设计 继电器电路设计 三极管 3月日更

架构学习(2021年03月06日)

张小胖

电商管理系统之发票子系统设计(二)

长沙造纸农

架构设计 高并发系统设计 电商 电子发票 发票

新消费品品牌的崛起给户外广告带来了哪些新机遇?

󠀛Ferry

七日更 3月日更

Elasticsearch Mapping Root Object

escray

elastic 七日更 28天写作 死磕Elasticsearch 60天通过Elastic认证考试 3月日更

MySQL 数据查询语言(DQL)& 事务控制语言(TCL)详解

若尘

MySQL

专访 | 我与毕玄大师的对话

高翔龙

Java 阿里巴巴 中间件 架构师 访谈录

(28DW-S8-Day15) 在线教育的MOT

mtfelix

在线教育 28天写作 峰值体验 关键时刻 MOT

要拥有必先懂失去怎接受——浅谈前景理论

Justin

心理学 28天写作 游戏设计

【LeetCode】分割回文串Java题解

HQ数字卡

算法 LeetCode 28天写作

超干货 (实战经验)结合公司业务分析离线数仓建设实践

五分钟学大数据

大数据 数据仓库 28天写作 3月日更

写作平台的一些乱象

ES_her0

28天写作 3月日更

kvm

梅花鹿鹿

kvm

最全Hive SQL语法、Hive函数及使用注意事项(一)

五分钟学大数据

大数据 Hive SQL 28天写作 3月日更

Docker部署ClickHouse监控平台

wjchenge

关于 Python 中的字符串,我在补充两点,滚雪球学 Python

梦想橡皮擦

Python 28天写作 3月日更

旧区块链思维面临淘汰

CECBC区块链专委会

区块链

18 个 Java8 日期处理的实践,太有用了!

xcbeyond

Java java8 日期处理 3月日更

常见的设计模式原则

一个大红包

设计模式 设计原则 28天挑战 3月日更

详解CQRS架构模式-InfoQ