什么是CQRS?

这个问题网上可以找到很多资料,未接触过的童鞋请先查看Udi DahanGrey YoungRinat Abdullin,园子里dax.net,以及Jdon社区上的相关文章。

例如下面几篇文章:

1. http://www.cnblogs.com/daxnet/archive/2011/01/06/1929099.html

1. http://www.udidahan.com/2009/12/09/clarified-cqrs/

2. http://www.jdon.com/jivejdon/thread/37891

这里只通过Udi Dahan的《Clarified CQRS》文章中的一张图片简要介绍一下:

UI上有两种类型的操作:命令和查询,例如显示销量最好的5个产品就属于查询,而提交一个订单、修改密码等则属于命令。因为大部分系统都是读多写少,而且业务逻辑基本都出现在写入的一端,所以查询和命令的分离可以让我们独立的去优化查询。

查询 (Query)

上图中,可以看到Query不是通过DB来查询,而是通过一个专门用于查询的Read DB(上图中的Cache,它不一定是数据库,但为方便起见,下面统称Read DB),Read DB中的表(方便起见,暂且认为这个Read DB是一个RDBMS)是专门针对UI优化过的,例如里面可能会有LatestProductListModel(ProductId, ProductName, Price, BrandName, AddedTime)、BestSoldProductListModel(ProductId, ProductName, TotalSold)这样的表,分别表示最新的产品列表,销量最好的产品列表(它们其实就相当于是View Model)。LatestProductListModel中有一个BrandName的字段,注意,不是BrandId,因此,对于界面中的查询,几乎全都可以通过SELECT * FROM [TABLE]这样的SQL语句来实现,可能有少数Where,但基本没有Join,这对于界面的加载速度绝对是有利无弊的(其实也是在用空间换时间)。

命令 (Command)

业务逻辑大部分都发生在写入的时候,例如用户购买商品提交订单时,我们要验证库存,用户信息订单数据是否有效等。如果从传统DDD的角度看,Command类似于Application Service,用户的命令(如提交订单)会以Command的形式得到执行,而Command中也不会带有业务逻辑,Command中做的事情基本上是:通过Repository得到相关的领域对象,调用某些领域服务(Domain Service)执行一些操作(业务逻辑都将保留在领域模型中),然后执行Commit或SaveChanges之类的方法提交改动,之后,相关的数据就会写入到Write DB中(图的DB,下文统称Write DB)。需要注意的是,UI上的查询都是查Read DB,而不是Write DB。

领域模型 (Domain Model)

这和Evans的DDD中说的领域模型没有太多区别,是“the heart of software”。

领域事件 (Domain Event)

领域事件占据的地位非常重要,不仅限于CQRS。相信会有一部分人曾和我一样碰到过这样的问题:

Account实体(表示帐户)有个Balance属性(表示帐户余额),我们一般不会公开这个属性的setter,而是通过写一些IncreaseBalance(decimal amount)之类的方法来实现帐户余额的变动。

这时问题就来了,我们想在帐户变动时添加一条AccountLog记录,但Log记录成千上万,我们不能直接通过ORM的一对多映射把AccountLog集合实现成Account的一个集合属性,那我们就需要在IncreaseBalance()中得到AccountLogRepository,这样才有办法插入AccountLog(从DDD的角度,AccountLog不是聚合根,所以不能有AccountLogRepository,但在性能影响严重的时候,也只好做些取舍了)。

不管用了依赖注入还是什么的,总之,Account已经依赖上Repository了,这就让领域对象变得很不纯净,并且,假如我们以后不仅要记录log,还要短信通知用户呢?那要修改源代码吗?这也很不OCP。

而领域事件正好可以解决这种问题:只要在IncreaseBalance()方法的末尾,触发一个领域事件,然后我们独立写一个EventHandler的类去实现log的添加(框架可以保证EventHandler可以和领域事件绑定到一起)。

回到CQRS,因为Command将数据写到了Write DB中,而UI查询的是Read DB,那我们就需要用某种方式实现这两个数据库的同步,解决办法已经很明显了,写一堆的EventHandler类去监听领域事件。例如我们有一个更改产品价格的命令ChangePriceCommand,它执行后,一个叫做PriceChangedEvent会被触发,那我们只要写一个PirceChangedEventHandler的类,在这里面将Read DB中相关的价格信息更改到最新值即可实现同步(这里会涉及到Read DB中表结构改变的问题,后面再说)。

结语

CQRS有意思的地方还不只这些,还有常和CQRS一起讨论的Event Sourcing(事件溯源,下面简称ES)等。

总得来说,CQRS看起来很迷人,但在自己的实践过程中,碰到了各种各样的问题,尤其ES,这几乎颠覆了平常的开发思维。例如,使用了ES后,领域模型只能通过Id来查询,如果你想查询姓名为“水哥”的用户,是做不到的,因为不会存在一个叫做User的表。相信大部分刚接触ES的朋友都会对此感到不适应。这需要思维上的改变。

后续的几篇文章里,我会继续分享自己在CQRS实践过程中碰到的各种感觉比较典型的问题以及我目前能找到的最好方案(更希望到时有童鞋有更好的方案分享)。然后通过实现一个迷你型的CQRS框架以及基于其开发的一个BookStore示例项目来展示CQRS所带来的好处。

这个迷你框架和示例项目中将会对常讨论的CQRS进行简化,剔除掉个人感觉和平常开发跨度比较大的东西,例如ES,异步Command等,同时还会针对平常习惯的开发方案做一些取舍,例如UI中可以根据需要混合查询Read DB和Write DB(前提是在Write DB的查询也很简单的情况下,比如同样只需要一个SELECT)。

[转] (CQRS)命令和查询责任分离架构模式(一) 之 什么是CQRS的更多相关文章

  1. [转] (CQRS)命令和查询责任分离架构模式(二) 之 Command的实现

    概述 继续引用上篇文章中的图片(来源于Udi Dahan博客),UI中的写入操作都将被封装为一个命令中,发送给Domain Model来处理. 我们遵循Domain Driven Design的设计思 ...

  2. 转:命令和查询责任分离(CQRS)架构模式

    读了“蓝皮书”距今差不多一年,它改变了我的软件开发和构建软件架构观.在我作为一名程序员期间,我尝试了许多不同的方式来构建软件.方法有很多,包括一个贫血的域模型(Anemic Domain Model) ...

  3. 危险!水很深,让叔来 —— 谈谈命令查询权责分离模式(CQRS)

    多年以前,那时我正年轻,做技术如鱼得水,甚至一度希望自己能当一辈子的一线程序员. 但是我又有两个小愿望想要达成:一个是想多挣点钱:另一个就是对项目的技术栈和架构选型能多有点主动权. 多挣点钱是因为当时 ...

  4. 架构模式: 命令查询职责分离 (CQRS)

    架构模式: 命令查询职责分离 (CQRS) 问题 如何在微服务架构中实现查询 结论 将应用程序拆分为两部分:命令端和查询端.命令端处理创建,更新和删除请求,并在数据更改时发出事件.查询端通过对一个或多 ...

  5. Command and Query Responsibility Segregation (CQRS) Pattern 命令和查询职责分离(CQRS)模式

    Segregate operations that read data from operations that update data by using separate interfaces. T ...

  6. 云计算设计模式(六)——命令和查询职责分离(CQRS)模式

    云计算设计模式(六)——命令和查询职责分离(CQRS)模式 隔离,通过使用不同的接口,从操作读取数据更新数据的操作.这种模式可以最大限度地提高性能,可扩展性和安全性;支持系统在通过较高的灵活性,时间的 ...

  7. MySQL读写分离-架构

    MySQL读写分离-架构 简介 对于很多大型网站(pv值百万.千万)来说,在所处理的业务中,其中有70%的业务是查询(select)相关的业务操作(新闻网站,插入一条新闻.查询操作),剩下的则是写(i ...

  8. ElasticSearch实战系列十: ElasticSearch冷热分离架构

    前言 本文主要介绍ElasticSearch冷热分离架构以及实现. 冷热分离架构介绍 冷热分离是目前ES非常火的一个架构,它充分的利用的集群机器的优劣来实现资源的调度分配.ES集群的索引写入及查询速度 ...

  9. Oracle读写分离架构

    读写分离是架构分布式系统的一个重要思想.不少系统整体处理能力并不能同业务的增长保持同步,因此势必会带来瓶颈,单纯的升级硬件并不能一劳永逸.针对业务类型特点,需要从架构模式上进行一系列的调整,比如业务模 ...

随机推荐

  1. [ext4]磁盘布局 - group分析

    ext4文件系统的磁盘布局是先把磁盘分成一个个相同大小的block块(每个block块的大小默认是4K),然后把这些block块合成一个个group. group大小最大为256M(默认为256M), ...

  2. Composer 中国全量镜像(二)

    一.查看当前镜像地址 在命令行输入如下命令,即可查看镜像地址: $ composer config -g repo.packagist {"type":"composer ...

  3. hadoop集群搭建--CentOS部署Hadoop服务

    在了解了Hadoop的相关知识后,接下来就是Hadoop环境的搭建,搭建Hadoop环境是正式学习大数据的开始,接下来就开始搭建环境!我们用到环境为:VMware 12+CentOS6.4 hadoo ...

  4. linux系统资源限制

    概述 拜读了小Y对ora-7445问题的解读,猛然惊醒发现自己对oracle运行在linux上的资源限制一直是朦朦胧胧的,以此文章督促自己深入了解. 1 limits.conf 和sysctl.con ...

  5. 广义后缀树(GST)算法的简介

    导言 最近软件安全课上,讲病毒特征码的提取时,老师讲了一下GST算法.这里就做个小总结. 简介 基本信息  广义后缀树的英文为Generalized Suffix Tree,简称GST. 算法目的   ...

  6. 编写自己的一个简单的web容器(二)

    昨天我们已经能够确定浏览器的请求能够被我们自己编写的服务类所接收并且我们服务类响应的数据也能够正常发送到浏览器客户端,那么我们今天要解决的问题就是让我们的数据能够被浏览器识别并解析. Http(Htt ...

  7. Windows8.1 + Nvidia cuda8.0 + Vs2015

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Vs2015 1.查看本机配置,查看显卡类型是否支持NVIDIA GPU选中计算机-->右键属性-->设备管理 ...

  8. 前端魔法堂:屏蔽Backspace导致页面回退

    前言  前几天用户反映在录入资料时一不小心错按Backspace键,就会直接回退到是一个页面,导致之前辛辛苦苦录入的资料全部丢失了.哦?居然还有这种情况.下面我们来一起探讨一下吧! Windows系统 ...

  9. 前端架构之路:使用Vue.js开始第一个项目

    Vue.js做为目前前端最热门的库之一,为快速构建并开发前端项目多了一种思维模式.本文通过一个简单的实例开始上手Vue.js开发.   一.技术准备 笔者建议在开始项目前,对以下两个技术点进行了解. ...

  10. Good Vegetable 4级算法题 分值: [320/3120] 问题: [8/78]

    1523 非回文 题目来源: CodeForces 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 收藏 关注 一个字符串是非回文的,当且仅当,他只由前p个小写字母 ...