阅读本文约需要5分钟


今天主要跟大家分享一下什么是 CQRS,以及在项目中如何去使用。

1. CRUD系统

我们平常最熟悉的就是三层架构,通常都是通过数据访问层来修改或者查询数据,一般修改和查询使用的是相同的实体。然后通过业务层来处理业务逻辑,将处理结果封装成DTO对象返回给控制层,再通过前端渲染。反之亦然。

这里基本上是围绕关系数据库构建而成的“创建、读取、更新、删除”系统(即CRUD系统),此类系统在一些业务逻辑简单的项目中可能没有什么问题,但是随着系统逻辑变得复杂,用户增多,这种设计就会出现一些性能问题。

我们经常用到的解决方案就是对数据库进行读写分离。让主数据库处理事务性的增、删、改操作,让从数据库处理查询操作,然后主从数据库之间进行同步。但是这只是从DB角度处理了读写分离,从业务或者系统层面上来说,读和写的逻辑仍然是存放在一起的,他们都是操作同一个实体对象。

这时候,CQRS 就该登场了。

2. CQRS系统

简单的说,CQRS(Command Query Responsibility Segration)就是一个系统,从架构上把 CRUD 系统拆分为两部分:命令(Command)处理和查询(Query)处理。其中命令处理包括增、删、改。

然后命令与查询两边可以用不同的架构实现,以实现CQ两端(即Command Side,简称C端;Query Side,简称Q端)的分别优化。两边所涉及到的实体对象也可以不同,从而继续演变成下面这样。

当然了,CQRS 作为一个读写分离思想的架构,在数据存储方面,也没有做过多的约束。所以 CQRS可以有不同层次的实现。

3. CQRS 实现方式

CQRS 可以有两种实现方式。

1)CQ 两端数据库共享,只是在上层代码上分离。这样做的好处是可以让我们的代码读写分离,更容易维护,而且不存在 CQ 两端的数据一致性问题,因为是共享一个数据库的。这种架构是非常实用的(也就是我上面画的那种)。

2)CQ 两端不仅代码分离,数据库也分离,然后Q端数据由C端同步过来。同步方式有两种:同步或异步,如果需要 CQ 两端的强一致性,则需要用同步;如果能接受 CQ 两端数据的最终一致性,则可以使用异步。C端可以采用Event Sourcing(简称ES)模式,所有C端的最新数据全部用 Domain Event 表达即可;而要查询显示用的数据,则从Q端的 ReadDB(关系型数据库)查询即可(详细可以参考文献3)。

4. CQRS 的简单实现

说了这么多,该怎么实现呢?我们以上面提到的第一种方式为例:代码层面实现分离,数据库共享。这种方式在企业里也非常实用。

首先有几个概念需要介绍一下,CQRS 模式中,首先需要有 Command,这个 Command 命令会对应一个实体和一个命令的执行类。那整个系统中肯定有很多不同的 Command,那么还需要一个 CommandBus 来做命令的分发处理。

可能大家觉得比较抽象,我来写几行示例代码,一看就明白了。假设有个订单模块,我要新增一个订单信息。那么根据上文的分析,需要有个新增命令以及对应的订单实体(并不一定和数据库的订单实体完全对应)。首先先创建一个命令接口(绑定命令对应的实体),接口内部有个该命令的处理方法。

public interface Command<T> {     Object execute(T commandModel);}interface Command<T> {    Object execute(T commandModel);}

OK,接下来我们可以创建订单的新增命令了。

@Componentpublic class CreateOrderCommand implements Command<CreateOrderModel> {     @Override     public Object execute(CreateOrderModel model) {         // 具体的逻辑    }}

到这里,我们写好了具体的创建订单命令的逻辑,那么该命令需要放到 CommandBus 中去执行,所以我们要写这个 CommandBus。

@Componentpublic class CommandBus {     public <T> Object dispatch(Command<T> cmd, T model) {         return cmd.excute(model);    }}public class CommandBus {    public <T> Object dispatch(Command<T> cmd, T model) {        return cmd.excute(model);    }}

可能大家会看着有点晕,甚至有点绕,没关系,我解释一下:这个 dispatch 方法就相当于分发执行,内部根据传入的具体 Command 以及对应的 model,去执行该 Command 实现的逻辑。

好了,那我们在熟悉的 Controller 层该如何去调用呢?很简单,如下:

@RestController@RequestMapping(value = "/order")public class OrderController {     @Resource     private GetOrderInfoService getOrderInfoService;     @Resource     private CreateOrderCommand createOrderCommand;     @Resource     private CommandBus commandBus;     @PostMapping(value = "/getInfo")     public Object getOrderInfo(GetOrderInfoModel model) {         return getOrderInfoService.getOrderInfos(model);    }     @PostMapping(value = "/creat")     public Object createOrderInfo(CreateOrderModel model) {         return commandBus.dispatch(createOrderCommand, model);    }}@RequestMapping(value = "/order")public class OrderController {

    @Resource    private GetOrderInfoService getOrderInfoService;    @Resource    private CreateOrderCommand createOrderCommand;    @Resource    private CommandBus commandBus;

    @PostMapping(value = "/getInfo")    public Object getOrderInfo(GetOrderInfoModel model) {        return getOrderInfoService.getOrderInfos(model);    }

    @PostMapping(value = "/creat")    public Object createOrderInfo(CreateOrderModel model) {        return commandBus.dispatch(createOrderCommand, model);    }}

我还写了一个获取订单信息的接口,大家有没有发现,查询和插入是不同的方式,插入走的是 CommandBus 分发到 CreateOrderCommand 去执行,而查询则是直接走 service 层去查。这就是 CQRS 模式。

当然了,当命令越来越多的时候,也可以将 CommandBus 抽象出接口,可以根据业务需求,实现多个不同的 CommandBus 来分发命令。

除此之外,CQRS 还可以用在任务调度模块中,不同的任务可以包含不同的 Command,实际中运用是非常广泛的。

5. 总结

CQRS 是一种思想很简单清晰的设计模式,他通过在业务上分离操作和查询来使得系统具有更好的可扩展性及性能,使得能够对系统的不同部分进行扩展和优化。在 CQRS 中,所有的涉及到对 DB 的操作都是通过发送 Command,然后特定的 Command 触发对应事件来完成操作,也可以做成异步的,主要看业务上的需求了。

CQRS 虽然在思想上简单,但是实现上相对来说复杂些,也涉及到 DDD 的一些概念了,当然了,这篇文章主要是介绍以及演示 CQRS 模式的基本实践,更多知识需要大家再深入的去学习。

最后,希望阅读完本文,能对你有所帮助。

参考文献:

1.https://msdn.microsoft.com/magazine/mt703431

2.https://blog.csdn.net/rocketluoqq/article/details/81434871

3.https://docs.microsoft.com/en-us/previous-versions/msp-n-p/dn589792(v=pandp.10)

4.https://www.cnblogs.com/yangecnu/p/Introduction-CQRS.html


往期阅读

狗屎一样的代码!快,重构我!

阿里面试,我挂在了第四轮……

同样是程序员,为什么别人比你更优秀?

我是如何从通信转到Java软件开发工程师的?

2018年所有精华文章汇总,错过了血亏!

关注我

每天进步一点点

点赞是最大的支持 

程序员除了会CRUD之外,还应该知道什么叫CQRS!相关推荐

  1. 程序员除了会 CRUD 之外,还应该知道什么叫 CQRS!

    作者 | 倪升武 责编 | 郭   芮 今天主要跟大家分享一下什么是 CQRS,以及在项目中如何去使用. CRUD系统 我们平常最熟悉的就是三层架构,通常都是通过数据访问层来修改或者查询数据,一般修改 ...

  2. 程序员,你的身体还健康吗?

    程序员不止眼前的逻辑和代码,还应有健康的体魄和精气神. 程序猿 对大多数程序猿来说,生活没那么多诗和远方,只有加不完的班,写不完的代码和修不完的bug. 大多数人对程序猿的看法应该是生活随便,只专注于 ...

  3. 36岁转行学java_年纪大了还想转行当程序员,现在学java还来得及吗

    现在流行这样一种论调:说程序员就是吃青春饭的,年纪大了就不要再转行当java程序员了.可是仍然还有许多人前赴后继的想来学java当程序员,但是心里面有隐约担心:年纪也不小的,快30了还能学java吗? ...

  4. 学了go语言再学java容易吗_年纪大了还想转行当程序员,现在学java还来得及吗-Go语言中文社区...

    现在流行这样一种论调:说程序员就是吃青春饭的,年纪大了就不要再转行当java程序员了.可是仍然还有许多人前赴后继的想来学java当程序员,但是心里面有隐约担心:年纪也不小的,快30了还能学java吗? ...

  5. 【PyTorch】 99%程序员都不知道, 深度学习还能这样玩 (建议收藏)

    [PyTorch] 99%程序员都不知道, 深度学习还能这样玩 概述 迁移学习 入住 GitHub 项目详解 get_data.py (获取数据) get_model (获取模型) 参数详解 使用说明 ...

  6. 谷歌程序员辞职创业,赚钱还没原来多,码农工资有泡沫吗?

    程序员是真正的高薪行业.近年来随着AI行业的兴起,工资更是高到吓人,有公司甚至愿意为AI工程师开出百万年薪.关于这个现象,不仅我国人民喜闻乐见,美国人民也争论不休. 最近一位名叫Jack Wilson ...

  7. 程序员工作压力大,为什么还这么多人想做程序员?是因为喜欢吗?

    最近过年遇到很多亲戚和朋友,不知道大家的身边是怎样的,阿粉身边的亲戚朋友从事计算机相关的还是挺少的,很多还是从事一些传统行业. 最近跟一个亲戚聊天的时候就聊到网上对程序员的一些刻板影响,什么格子衫呀, ...

  8. 程序员一亩三分地之外

    Photo by sergio souza on Unsplash 作为一个程序员,甚至我可以说我是一个资深程序员,我总是关注于各种技术,各种工具,今天研究一下,明天研究一下那个,好像在我的一亩三分地 ...

  9. 第二十七期:网络爬虫程序员被抓,我们还敢爬虫吗?细数那些Java爬虫技术

    最近,某大数据科技公司因为涉嫌非法抓取某招聘网站用户的简历信息,公司被查封,负责编写抓取程序的程序员也将面临坐牢. 作者:架构之路来源 最近,某大数据科技公司因为涉嫌非法抓取某招聘网站用户的简历信息, ...

最新文章

  1. java按特殊标志截取_java 字符串分割处理split及特殊符号
  2. SQL语句获取数据库名、所有表名、所有字段名及字段类型
  3. 菜菜sklearn——XGBoost(1)
  4. [firefox] Scrapbook Plus的改进版Scrapbook X
  5. 集训队脱单大法:这是一道只能由学姐我自己出数据的水题
  6. Error(s) in loading state_dict for ResNet 问题解决
  7. 邮件服务器在企业网中的应用
  8. vlookup练习_大胆合并吧!VLOOKUP坐字法专做单元格合并查找
  9. linux目录硬链接,linux查看硬链接对应的所有文件
  10. 面试准备每日系列:计算机底层之并发编程(一)原子性、atomic、CAS、ABA、可见性、有序性、指令重排、volatile、内存屏障、缓存一致性、四核八线程
  11. 深入理解JVM(重要)
  12. 通达信公式改写成python代码
  13. 19个免费好用的CSS代码样式生成器工具
  14. MTK 平台屏蔽 factory mode
  15. [转帖]历史上真实的《勇敢的心》
  16. 解决pycharm终端/cmd运行python脚本报错“ImportError/ModuleNotFoundError:No Module named ...”
  17. php f4v元数据,IIS设置支持flv,f4v,mp4,ogv,webm
  18. Unity开发——随笔1.0:关于LookAt()转向生硬解决办法
  19. 实战 SQL:销售数据的小计/合计/总计以及数据透视表
  20. iOS第三方登录之Twitter(登录,获取用户信息)含demo

热门文章

  1. 股票API下单接口是怎样传入交易数据的?
  2. Java并发指南14:Java并发容器ConcurrentSkipListMap与CopyOnWriteArrayList
  3. 什么是双线机房??双线是怎么实现的!!!
  4. CyberArticle(eLib电子图书馆)网文快捕
  5. 【渝粤题库】陕西师范大学201291 商法学 作业(高起专)
  6. 一个 Python 的轻量级搜索工具 -- Whose
  7. Vue开发问题记录:expected indentation of 0 spaces but found 2
  8. 家用是买轿车还是suv_SUV的完整形式是什么?
  9. 记录一次服务器CPU负载高,利用率正常的处理方法
  10. windows环境下kafka-console-consumer.bat接受中文乱码问题