一种简单的CQRS架构设计及其实现
一、为什么要实践领域驱动?
近一年时间我一直在思考一个问题:“如何设计一个松耦合、高伸缩性、易于维护的架构?”。之所以有这样的想法是因为我接触的不少项目都是以数据库脚本来实现业务逻辑。在项目初期使用这样的方式野蛮开发似乎显得很高效,但是大家其实都清楚,正是这样的项目让大家拖入了加班的深渊。这种系统维护性差,无法扩展,无法编写有效的单元测试,质量基本没有保证。
一个符合我心理预期的架构,一定不是靠使用某个代码生成工具来完成的,这样的项目把码农培养成了彻头彻尾没有思想的个体。一个有追求的码农,请远离只会让你使用代码生成工具的“架构师”。
既然数据库驱动的开发方式存在一些致命的问题,我们很自然的想到了领域驱动开发(DDD)。在《我眼中的领域驱动开发》一文中,我提到了领域驱动设计不同于传统软件开发的一些思考方式。这些思想看起来很简单,但是真正实践起,思维的转变还是有一定的难度。
在我职业生涯里遇到的每一个同事,在讨论问题时都能够表现出自己擅长的一面,有的人思路比较清晰,有的人想法比较特别,跟这些人在一起讨论方案往往都会够给你灵感。领域驱动开发这种方式需要融入有生命力的想法,每一个人都可以参与到设计当中,软件开发才会变成一件有意思的事情。
二、使用EF来实践领域驱动
我曾尝试使用单纯的EF来实践领域驱动开发,老实说,我没法实践成功。我想忘掉数据库,但是很难。为什么这样说?我们知道领域驱动讲求以领域模型为基本单位思考问题,比如一次购物过程可能是一个领域模型,我抛开了数据库,建立的模型。这时来了一个需求,一个界面需要能够查询一个范围内的购物记录,可能还有一些复杂的条件,这时候我的领域模型并不能很好的支持这样的查询,因为我在这之前压根没有考虑表结构如何设计,我的领域模型不是为查询设计的,所以这样的一个查询很容易存在性能问题。所以忘掉数据库这一命题很难实现。
三、为什么会存在这样的问题?
发生了这样的问题我就在思考到底是哪里出了问题,是领域驱动开发这种软件开发方式存在问题吗?
造成这种局面的根本原因在于“我们给Domain强加了查询的职责,长久以来的软件开发模式太过于依赖于关系数据库”,关系数据库的首要任务是持久化数据,查询只是他的部分能力,长久以来我们即想让关系数据库存储海量数据,又想让查询变得更高效。显然,我们对关系型数据库的要求有点过分了。
我想这也是Greg Young提出了CQRS架构的原因,当我们在领域模型中去掉搜索和查询的职责后,问题引刃而解。
四、实现CQRS有哪些难点?
既然CQRS才是实践DDD的最佳途径,那么我们就可以使用CQRS了么?提到CQRS,不得不提到博客园的两位CQRS元老级人物,dax.net和netfocus,他们两位都贡献了自己的CQRS框架,但是我猜测,即便是他们两位大神也不会轻易在真实场景下使用CQRS架构。在我看来要真正实践CQRS需要天时、地利、人和三者具备。
1、经典的CQRS是通过Event Sourcing来实现领域模型的还原和Query端的同步,这就要求领域模型的设计一定要一次到位,一旦Event被持久化到生产环境,这时候再修改设计就会带来极大的难度。
2、对开发人员要求非常之高、要建立这样的一直团队需要长时间的磨合和培养。
3、采用CQRS的DDD由于对设计要求较高,所以在开发初期并不能很快看到效果,这就要求公司能够容忍、理解并且支持,这条似乎是最难符合条件的。
五、实现一个简单的CQRS方案
经过分析,要想成功实践领域驱动,查询与命令职责一定要分离,我们还是要实现CQRS,但是不再通过Event Sourcing来实现,从而减少复杂度。下面就是我的一个不太成熟的想法:
1、Domain通过EF持久化。
2、每个Domain逻辑在实现中都将产生两种固定类型的Event,***CreatedEvent和***UpdatedEvent。
3、由于取消了Event Sourcing,所以降低了Domain设计的复杂度,此时的关系型数据就相当于最新的快照。
4、在EF的UnitOfWork执行成功后注册发布事件的回调,确保领域模型持久化成功才发布事件。
5、事件发布到双工ServiceBus中,双工ServiceBus包含一个InMemory的同步ServiceBus和一个支持消息队列的异步ServiceBus。
6、领域发布事件后,先有InMemory的同步ServiceBus更新由Redis实现的QueryModel,确保界面会同步刷新,单个Domain的读取将通过Redis来实现。紧接着消息会发送到支持消息队列的异步ServiceBus。
7、消息进入消息队列后供其他子系统消费,同时更新Elastic Search,界面的复杂搜索将通过Elastic Search来实现。
8、此时的系统已经演化为一个松耦合、可扩展、基于领域模型的分布式架构方案。
当查询和读取Domain的职责被分摊到Elastic Search和Redis后,这时候再设计领域模型就不会有外界因素来干扰你。
另外整个系统不会出现一句sql,让sql这种反人类的语言去见鬼吧,此时要是还有谁要是觉得“必须得写sql,sql效率高”。你过来,我保证不打死你。o(^▽^)o
目前的Demo基于以上的想法正在一步步实现中,请关注。另外,由于水平有限,整个架构设计难免有不合理的地方,欢迎提出宝贵意见。
https://git.oschina.net/richieyangs/BookLibrary
一种简单的CQRS架构设计及其实现的更多相关文章
- CQRS架构设计及其实现
CQRS架构设计及其实现 一.为什么要实践领域驱动? 近一年时间我一直在思考一个问题:“如何设计一个松耦合.高伸缩性.易于维护的架构?”.之所以有这样的想法是因为我接触的不少项目都是以数据库脚本来实现 ...
- .net mvc中一种简单的工作流的设计
开篇前的废话:工作流是我们在做互联网应用开发时经常需要用到的一种技术,复杂的工作流我们基本是借助一些开源的 工作流项目来做,比如 ccflow等,但是有时候,我们只需要实现一些简单的工作流流程,这时候 ...
- SpringBank 开发日志 一种简单的拦截器设计实现
当交易由Action进入Service之前,需要根据不同的Service实际负责业务的不同,真正执行Service的业务逻辑之前,做一些检查工作.这样的拦截器应该是基于配置的,与Service关联起来 ...
- 架构设计:一种远程调用服务的设计构思(zookeeper的一种应用实践)
在深入学习zookeeper我想先给大家介绍一个和zookeeper相关的应用实例,我把这个实例命名为远程调用服务.通过对这种应用实例的描述,我们会对zookeeper应用场景会有深入的了解. 远程调 ...
- DDD CQRS架构和传统架构的优缺点比较
明天就是大年三十了,今天在家有空,想集中整理一下CQRS架构的特点以及相比传统架构的优缺点分析.先提前祝大家猴年新春快乐.万事如意.身体健康! 最近几年,在DDD的领域,我们经常会看到CQRS架构的概 ...
- ENode框架Conference案例分析系列之 - 架构设计
Conference架构概述 先贴一下Conference案例的在线地址,UI因为完全拿了微软的实现,所以都是英文的,以后我有空再改为中文的. Conference后台会议管理:http://www. ...
- 微软&中科大提出新型自动神经架构设计方法NAO
近期,来自微软和中国科学技术大学的刘铁岩等人发表论文,介绍了一种新型自动神经架构设计方法 NAO,该方法由三个部分组成:编码器.预测器和解码器.实验证明,该方法所发现的架构在 CIFAR-10 上的图 ...
- .net架构设计读书笔记--第三章 第10节 命令职责分离(CQRS)简介(Introducing CQRS)
一.分离查询命令 Separating commands from queries 早期的面向DDD设计方法的难点是如何设计一个类,这个类要包含域的方方面面.通常来说,任务软件系统方法调用可以 ...
- 一个简单可参考的API网关架构设计
网关一词较早出现在网络设备里面,比如两个相互独立的局域网段之间通过路由器或者桥接设备进行通信, 这中间的路由或者桥接设备我们称之为网关. 相应的 API 网关将各系统对外暴露的服务聚合起来,所有要调用 ...
随机推荐
- 关于 Chrome 浏览器中 onresize 事件的 Bug
我在写插件时用到了 onresize 事件,在反复地测试后发现该事件在 Chrome 及 Opera(内核基本与 Chrome 相同,以下统称 Chrome)浏览器打开时就会执行,这种情况也许不能算作 ...
- 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例
前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...
- 微信公众号开发之VS远程调试
目录 (一)微信公众号开发之VS远程调试 (二)微信公众号开发之基础梳理 (三)微信公众号开发之自动消息回复和自定义菜单 前言 微信公众平台消息接口的工作原理大概可以这样理解:从用户端到公众号端一个流 ...
- REST简介
一说到REST,我想大家的第一反应就是“啊,就是那种前后台通信方式.”但是在要求详细讲述它所提出的各个约束,以及如何开始搭建REST服务时,却很少有人能够清晰地说出它到底是什么,需要遵守什么样的准则. ...
- 探索ASP.NET MVC5系列之~~~4.模型篇---包含模型常用特性和过度提交防御
其实任何资料里面的任何知识点都无所谓,都是不重要的,重要的是学习方法,自行摸索的过程(不妥之处欢迎指正) 汇总:http://www.cnblogs.com/dunitian/p/4822808.ht ...
- FineBI:一个简单易用的自助BI工具
过去,有关企业数据分析的重担都压在IT部门,传统BI分析更多面向的是具有IT背景的人员.但随着业务分析需求的增加,很多公司都希望为业务用户提供自助分析服务,将分析工作落实到业务人员手中.但同时,分析工 ...
- BPM配置故事之案例3-参与者与数据自动加载
这才过了两天,阿海又来了. 阿海:公司决定改进管理方式,以后物资申请的申请人和申请部门要写具体使用人的名字和部门了. 小明:不是要让我改回去吧? 阿海:那太麻烦了,你能不能把申请人改成选择,选好人自动 ...
- Android之Dedug--Circular dependencies cannot exist in AnimatorSet
今日,在学习AnimatorSet时,使用play.with.after.before时,代码书写如下: ObjectAnimator animator1 = ObjectAnimator.ofFlo ...
- Selenium的PO模式(Page Object Model)[python版]
Page Object Model 简称POM 普通的测试用例代码: .... #测试用例 def test_login_mail(self): driver = self.driver driv ...
- 全球HTTPS时代已来,你跟上了吗?
全球HTTPS时代已来,你跟上了吗? 互联网发展20多年,大家都习惯了在浏览器地址里输入HTTP格式的网址.但前两年,HTTPS逐渐取代HTTP,成为传输协议界的"新宠". 早 ...