CQRS被称为邪教?

简介: CQRS全称Command Query Responsibility Segregation

CQRS全称Command Query Responsibility Segregation

image.png

在CQRS中,来自客户端的命令通过单独的路径抵达命令模型,而查询操作则采用不同的数据源,这样的好处在于可以优化对查询数据的获取,比如用于展现、用于接口或报告的数据。

CQRS这些年火起来了,常被人挂在嘴边提起。为什么?因为DDD提倡富模型,但从资源库查找所有需要显示的数据是困难的,特别是在需要显示来自不同聚合类型与实例的数据时。领域越复杂,这种困难程度越大

有没有一种完全不同的方法可以将领域数据映射到界面显示中呢?答案正是CQRS。

From CRUD to CQRS[1]文章中,作者比对了CRUD模式与CQRS模式

CRUD

我们传统使用的CRUD风格:

image.png

这就是经典的CRUD应用模式。数据流在应用中是这样的:

image.png所有的业务服务与领域服务都在同一条数据流中完成数据的获取与更新操作。

CQRS

相对于CRUD,CQRS应用模型,会有两条数据流:读与写

image.png

写命令数据流负责创建/更新/删除领域模型

image.png

读数据流负责从数据源获取数据

image.png

CQRS风格整体大概有三种形式:

1、应用完全分割成两个部分:

image.gifimage.png

2、应用有一个通用的web api层,但业务层分割成两部分:

image.png

3、webapi与business都是通用,command和query在通用服务中创建

image.png

DDD实践指南[2]中也引入了CQRS的元素,进行command与query区分。一切都很完美。

然后,现实并非如此,徐昊老师却称CQRS是邪教

总结下来有两点原因:

1、模式滥用

CQRS是个模式,模式有适用场景,然而现在CQRS成了全能模式,尤其在实践DDD时,更是标配模式。

CQRS本身是一个对象接口设计原则,把get/find和mutable的set分离

然后自然扩张变成了服务接口设计原则

有人灵机一动,用domain model做command,用query model做查询和报表

整套系统两模型

这就是把一个限定概念无限扩大

到现在,有人说graphql做query,domain model做command

然而实际上,想解决的都是报表 特别是ad hoc报表

用一个特别复杂的方案,解决个简单的问题(还比一定解决得了),逢人就说,这是应该的方案,这不是邪教是什么

为什么说CQRS是复杂的?

Udi Dahan[3]绘制了一张图:

image.png

图中可以看到Cache,写模型也要读有cache,然后你的读模型也有cache,俩cache不一致

特别是用jpa/hibernate之类的框架,cache行为很难控制,不加cache性能不好,加了cache,俩模型不一致。

而且代码量也增加,从多个聚合取数据拼装一起的代码量多,你分成cqrs代码不多吗?多一套model,代码不多吗

cqrs作为模式,是一种分布式架构模式,一个单体里大概率就是用错了

Martin Fowler[4]也表明了这个观点:

“By separate models we most commonly mean different object models, probably running in different logical processes, perhaps on separate hardware. ”- Martin Fowler.

把repo分成xxxquery 放各种find和xxxupdate 放save,理论上也叫cqrs

底层模型还是一样 只是接口分一下,那有啥用

cqrs重点在有两套模型

如果是cqrs 我建议你用独立的查询数据库或者搜索引擎

一个最典型的cqrs,cms/blog系统,读模型,file缓存,搜索引擎;写模型,后台domain,分得干干净净

2、把view model当query model

把view model当query model,本来mvc,mvp,mvvm的问题,硬拗成cqrs

围绕domain做read model,第一选择是细化domain 调整各种聚合

必须在限定问题领域内使用

行业里说cqrs的,大部分是因为 domain model提炼不力,绕过domain直接查询数据库

何时使用

既然CQRS是种模式,就得像任何模式一样,有适用场景,也有不适用场景。

很多系统很适合CRUD模式,就应该使用CRUD,那么什么场景下适合CQRS呢?

Martin Fowler指出了两个场景

1、比较复杂的领域模型

这种场景需要强调的是,使用CQRS还是很少的场景。当读写模型比较重叠时,使用共享模式相对简单些。

不然会增加复杂性,从而减小生产力并增加风险。

2、高性能应用

CQRS可以隔离读与写负载,并独立扩容。

当读与写模型有明显区别时,会很方便。即使没有,读写也可以使用不同的优化策略。

总结

可以联想到在数据库架构时,也常使用主写从读架构。那是不是也称为CQRS呢?

我们在一个应用中,真的同时使用了这两种模型吗?其实也未必,只是某些小点,借鉴了CQRS思想。

CQRS作为模式,是一种分布式架构模式,而且是很复杂的模式。流行的CQRS不过是为了查询而绕开domain的做法,不过是因为domain提炼不到位。

正常的程序,都有读写功能,不需要分成皆然不同的两套模型,就无所谓是不是CQRS了。

这也说明了一个问题,大佬说的CQRS与普通人说的CQRS其实不是一个东西,普通人为了轻松理解,就简单理解为读写分离,并且自以为这就是全部,变成了全知全能,其实这只是个开始。

References

[1] From CRUD to CQRS: https://antonnalivayko1.medium.com/from-crud-to-cqrs-part1-cqrs-cbeac0350043

[2] DDD实践指南: https://www.zhuxingsheng.com/blog/ddd-tactical-practice-guide.html

[3] Udi Dahan: https://udidahan.com/2009/12/09/clarified-cqrs/

[4] Martin Fowler: https://martinfowler.com/bliki/CQRS.html



目录
相关文章
|
4月前
|
存储 前端开发 测试技术
DDD - 六边形架构和CQRS架构
DDD - 六边形架构和CQRS架构
270 0
|
4月前
|
设计模式 缓存 BI
软件体系结构 - CQRS
【4月更文挑战第23天】软件体系结构 - CQRS
46 0
|
存储 消息中间件 人工智能
领域事件与CQRS:分布式系统设计的新范式
领域事件与CQRS:分布式系统设计的新范式
|
存储 搜索推荐 NoSQL
「领域驱动设计」DDD,六边形架构,洋葱架构,整洁架构,CQRS的整合架构
「领域驱动设计」DDD,六边形架构,洋葱架构,整洁架构,CQRS的整合架构
|
消息中间件 存储 程序员
CQRS架构简介
CQRS架构简介
1139 0
CQRS架构简介
|
存储 JSON 自然语言处理
「领域驱动设计」DDD,六边形架构,洋葱架构,整洁架构和CQRS的整合(下)
「领域驱动设计」DDD,六边形架构,洋葱架构,整洁架构和CQRS的整合
|
存储 搜索推荐 NoSQL
「领域驱动设计」DDD,六边形架构,洋葱架构,整洁架构和CQRS的整合(上)
「领域驱动设计」DDD,六边形架构,洋葱架构,整洁架构和CQRS的整合
|
消息中间件 运维 JavaScript
架构设计30-架构模式03-事件驱动架构模式
架构设计30-架构模式03-事件驱动架构模式
199 0
架构设计30-架构模式03-事件驱动架构模式
|
存储 Kubernetes Cloud Native
事件驱动的分布式事务架构设计
在这个架构中,已经没有中心化事务协调者 TC Server,用户只需关心自身应用的高可用,如应用多副本部署,hptx 和 dbpack 会通过 etcd 选主,只有选为 master 的副本才能 watch 自身产生的分支事务数据去做提交、回滚,避免了提交、回滚逻辑重复执行的问题。集成 hptx,只需要依赖相应的 sdk,而不需要部署额外的 TC Server。全新的、云原生的、事件驱动架构,更加简洁,性能更强。采用 hptx 的应用事务协调性能比 Seata-Golang 提升 1 倍,通过 dbpack 以 mesh 方式协调分布式事务性能比 seata-golang 提升了百分之 50。
435 0
事件驱动的分布式事务架构设计