前言: CQRS
一词随着DDD
一同被大众所熟悉,但是你有没有想过CQRS
一词其实并非DDD
独有,非DDD
设计项目也能用,或许你正在用,但你不知道而已。本文介绍了一下CQRS
是什么,在DDD
中的作用,以及在项目中使用CQRS
的好处。
一、什么是CQRS?
先从概念开始,CQRS
即Command Query Responsibility Segregation
的缩写,译成中文就是“命令与查询职责分离”。
顾名思义在我们写CRUD代码的时候,其实是有可分为两类操作,一类是query
(查询),一类是command
(增删改)。
记住以下几点:
- 这个从分离关注点的角度来看,一个对数据无修改,一个则对数据有修改,二者的关注点是不同的,可以将其分离。
- 二者从代码结构上看,能够将聚合根的臃肿程度降低。
- 从性能上看,可以分别最大化读写的性能。
二、要不要用CQRS?
2.1 举例说明分层实践中的常规操作
不管你是否在进行DDD项目,其实你或多或少已经在接触或者使用CQRS
了。
上图的操作方式是不是很熟悉,相当常规的开发操作,Model
包里面建个model
,然后在DAO
层或者说Repository
层中对改model
进行增删改查操作。
一般而言这种操作是没有问题,或者说大家都是这么干的。那么我举个栗子,看看大家是否又能找到这种开发中熟悉的感觉!
例如:电商项目,商品(Goods)服务中的订单操作,开始只需要实现两个方法:
商品Repository
type GoodsRepository interface {
Save(ctx context.Context, order *model.Goods) error
GetByID(ctx context.Context, id int) (order *model.Goods, error)
}
好了,开启迭代模式:
- 需要展示商品详情,但是所需字段比
model.Goods
中的字段要少。 - 需要展示
Goods
列表,列表中所需要的信息比商品详情中更少。 - 根据时间、商品种类,标签、售价、利润···等维度对商品进行筛选展示。
- 展示商品的供应商,而供应商管理属于另一个业务服务
- 。。。
。。。
当你迭代完成,回头看你的代码,有没有发现:
- 你的
GoodsRepository
里面居然全是查询的操作代码。 - 为了应对不同的业务数据要求,需要将
model.Goods
中的数据进行裁剪与转换,这部分繁多的代码是放在Service
中,还是在Repository
中?是不是犯迷糊了,想想咱们DDD
系列的第二篇文章《六边形架构》,或许你会有所收获。
CQRS
通过单独读模型解决上面的问题
2.2 CQRS的不同模式
CQRS
中的读写分离可以分为两个层次:
-
代码层面的读写分离。
该模型的数据存在相同的DateBase
中,通过在业务代码中使用不同的分离后的读写模型,减少了繁多且又没法避免的数据转换操作。
- 存储层面的读写分离。
该CQRS
模型分离的更为彻底,不仅仅分离了读写模型,底层数据存储也一并分离了。读写操作分别写入不容的存储中,然后通过消息机制进行数据同步。
总结:
- 并不是项目中所有的
model
都需要进行CQRS
改造,并不是所有的CQRS
一定要将读写数据进行分离。 - 代码层面进行的
CQRS
有利于分离关注点,让代码层次结构更清晰,容易维护。 - 存储层次的
CQRS
应用于高性能查询场景,经常见于大型项目中MySQL
存储商品,然后用ES
查询商品等需要快速响应的场景。
三、Freedom中怎么使用CQRS?
目录
- golang领域模型-开篇
- golang领域模型-六边形架构
- golang领域模型-实体
- golang领域模型-资源库
- golang领域模型-依赖倒置
- golang领域模型-聚合根
- golang领域模型-CQRS
- golang领域模型-领域事件
项目代码 https://github.com/8treenet/freedom/tree/master/example/fshop
PS:关注公众号《从菜鸟到大佬》,发送消息“加群”或“领域模型”,加入DDD交流群,一起切磋DDD与代码的艺术!
有疑问加站长微信联系(非本文作者)