CQRS实践(1): 什么是CQRS
什么是CQRS?
这个问题网上可以找到很多资料,未接触过的童鞋请先查看Udi Dahan, Grey Young, Rinat 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)。
欢迎参与讨论,写的有问题的地方亦欢迎指正,嘿嘿。
转载于:https://www.cnblogs.com/mouhong-lin/archive/2012/03/23/what-is-cqrs.html
CQRS实践(1): 什么是CQRS相关推荐
- java cqrs架构_简单聊聊CQRS
序言 Domain Driven Design (DDD) CQRS Axonframework Mvc vs CQRS 参考 Domain driver design 领域驱动设计 也就是我们在 ...
- CQRS实践(3): Command执行结果的返回
上篇随笔讨论了CQRS中Command的一种基本实现. 面对UI中的各种命令,Controller会创建相应的Command对象,然后将其交给CommandBus,由CommandBus统一派发到相应 ...
- CQRS学习——最小单元的Cqrs(CommandEvent)[其一]
[说明:博主采用边写边思考的方式完成这一系列的博客,所以代码以附件为准,文中代码仅为了说明.] 结构 在学习和实现CQRS的过程中,首要参考的项目是这个[http://www.cnblogs.com/ ...
- 第三十五期:当我们在讨论CQRS时,我们在讨论些神马?
thz 6月18日 当我写下这个标题的时候,我就有些后悔了,题目有点大,不太好控制.但我还是打算尝试一下,通过这篇内容来说清楚CQRS模式,以及和这个模式关联的其它东西.希望我能说得清楚,你能看得明白 ...
- 深度长文:我对CQRS/EventSourcing架构的思考
开始之前想先说一下微服务架构和CQRS架构的区别和联系. 微服务架构现在很热,到处可以看到各大互联网公司的微服务实践的分享总结.但是,我今天的分享和微服务没有关系,希望可以带给大家一些新的东西. 如果 ...
- DDD - 六边形架构和CQRS架构
DDD - 六边形架构和CQRS架构 1. 六边形架构 2. CQRS 2.1 什么是CQRS 2.2 采用CQRS架构的一个前提 2.3 实现方式 2.4 CQRS的适用场景 2.5 CQRS架构的 ...
- 浅谈命令查询职责分离(CQRS)模式
浅谈命令查询职责分离(CQRS)模式 在常用的三层架构中,通常都是通过数据访问层来修改或者查询数据,一般修改和查询使用的是相同的实体.在一些业务逻辑简单的系统中可能没有什么问题,但是随着系统逻辑变得复 ...
- .NET现代化应用开发 - CQRS类目管理代码剖析
本周MASA Framework 进行了第四次课程直播,课程主题为类目管理的开发,直播中进行了理论讲解和实战演练(CQRS实践的演示可直达推文底部观看直播回放) 开始环节我们围绕三个点介绍CQRS的原 ...
- 详解 CQRS 架构模式
从一开始,软件系统就被用于各种用途,针对它们的需求也随着时间的推移而增长.需求的变更可能与业务逻辑.伸缩性或系统的其他方面有关. 为了满足这些相互矛盾或重叠的需求,工程师必须在设计系统时做出各种各样的 ...
最新文章
- 关于软件外包的一些看法(转)
- Python startswith() 方法
- 算法提高课-图论-单源最短路的综合应用-AcWing 342. 道路与航线:最短路dijkstra、拓扑排序 、综合题、好题
- thingsboard源码结构解析
- vue 怎么在字符串中指定位置插入字符_vue项目中在可编辑div光标位置插入内容的实现代码...
- Qt Creator编写代码
- Android_Kotlin 代码学习
- http协议工作原理(精简)
- 拓端tecdat|R语言在逻辑回归中求R square R方
- VC动态库可以嵌套调用
- Pytorch Note19 优化算法5 Adadelta算法
- 国开大学计算机实操,国开大学计算机实操答案一.doc
- 解决“error C1083: 无法打开包括文件: “HPSocket.h”: No such file or directory”
- PPO:Proximal Policy Optimization Algorithms
- 【MOD】函数判别性别
- C. Divan and bitwise operations
- aps生产排程解决家具用品业的难题
- 微信群裂变引流效果怎么样?微信社群引流怎么操作?
- 如何解决SQL Server2008不允许保存修改的问题
- 文件上传服务器取直链,云服务器直链