CQRS架构设计及其实现

一、为什么要实践领域驱动?

近一年时间我一直在思考一个问题:“如何设计一个松耦合、高伸缩性、易于维护的架构?”。之所以有这样的想法是因为我接触的不少项目都是以数据库脚本来实现业务逻辑。在项目初期使用这样的方式野蛮开发似乎显得很高效,但是大家其实都清楚,正是这样的项目让大家拖入了加班的深渊。这种系统维护性差,无法扩展,无法编写有效的单元测试,质量基本没有保证。

一个符合我心理预期的架构,一定不是靠使用某个代码生成工具来完成的,这样的项目把码农培养成了彻头彻尾没有思想的个体。一个有追求的码农,请远离只会让你使用代码生成工具的“架构师”。

既然数据库驱动的开发方式存在一些致命的问题,我们很自然的想到了领域驱动开发(DDD)。在《我眼中的领域驱动开发》一文中,我提到了领域驱动设计不同于传统软件开发的一些思考方式。这些思想看起来很简单,但是真正实践起,思维的转变还是有一定的难度。

在我职业生涯里遇到的每一个同事,在讨论问题时都能够表现出自己擅长的一面,有的人思路比较清晰,有的人想法比较特别,跟这些人在一起讨论方案往往都会够给你灵感。领域驱动开发这种方式需要融入有生命力的想法,每一个人都可以参与到设计当中,软件开发才会变成一件有意思的事情。

二、使用EF来实践领域驱动

我曾尝试使用单纯的EF来实践领域驱动开发,老实说,我没法实践成功。我想忘掉数据库,但是很难。为什么这样说?我们知道领域驱动讲求以领域模型为基本单位思考问题,比如一次购物过程可能是一个领域模型,我抛开了数据库,建立的模型。这时来了一个需求,一个界面需要能够查询一个范围内的购物记录,可能还有一些复杂的条件,这时候我的领域模型并不能很好的支持这样的查询,因为我在这之前压根没有考虑表结构如何设计,我的领域模型不是为查询设计的,所以这样的一个查询很容易存在性能问题。所以忘掉数据库这一命题很难实现。

三、为什么会存在这样的问题?

发生了这样的问题我就在思考到底是哪里出了问题,是领域驱动开发这种软件开发方式存在问题吗?

造成这种局面的根本原因在于“我们给Domain强加了查询的职责,长久以来的软件开发模式太过于依赖于关系数据库”,关系数据库的首要任务是持久化数据,查询只是他的部分能力,长久以来我们即想让关系数据库存储海量数据,又想让查询变得更高效。显然,我们对关系型数据库的要求有点过分了。

我想这也是Greg Young提出了CQRS架构的原因,当我们在领域模型中去掉搜索和查询的职责后,问题引刃而解。

四、实现CQRS有哪些难点?

既然CQRS才是实践DDD的最佳途径,那么我们就可以使用CQRS了么?提到CQRS,不得不提到博客园的两位CQRS元老级人物,dax.netnetfocus,他们两位都贡献了自己的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架构设计及其实现的更多相关文章

  1. 一种简单的CQRS架构设计及其实现

    一.为什么要实践领域驱动? 近一年时间我一直在思考一个问题:"如何设计一个松耦合.高伸缩性.易于维护的架构?".之所以有这样的想法是因为我接触的不少项目都是以数据库脚本来实现业务逻 ...

  2. DDD CQRS架构和传统架构的优缺点比较

    明天就是大年三十了,今天在家有空,想集中整理一下CQRS架构的特点以及相比传统架构的优缺点分析.先提前祝大家猴年新春快乐.万事如意.身体健康! 最近几年,在DDD的领域,我们经常会看到CQRS架构的概 ...

  3. 谈一下关于CQRS架构如何实现高性能

    CQRS架构简介 前不久,看到博客园一位园友写了一篇文章,其中的观点是,要想高性能,需要尽量:避开网络开销(IO),避开海量数据,避开资源争夺.对于这3点,我觉得很有道理.所以也想谈一下,CQRS架构 ...

  4. ENode框架Conference案例分析系列之 - 架构设计

    Conference架构概述 先贴一下Conference案例的在线地址,UI因为完全拿了微软的实现,所以都是英文的,以后我有空再改为中文的. Conference后台会议管理:http://www. ...

  5. .NET应用架构设计—面向查询的领域驱动设计实践(调整传统三层架构,外加维护型的业务开关)

    阅读目录: 1.背景介绍 2.在业务层中加入核心领域模型(引入DomainModel,让逻辑.数据有家可归,变成一个完整的业务对象) 3.统一协调层Application Layer(加入协调层来转换 ...

  6. CQRS架构如何实现高性能

    CQRS架构如何实现高性能 CQRS架构简介 前不久,看到博客园一位园友写了一篇文章,其中的观点是,要想高性能,需要尽量:避开网络开销(IO),避开海量数据,避开资源争夺.对于这3点,我觉得很有道理. ...

  7. CQRS架构

    CQRS架构 命令查询的责任分离Command Query Responsibility Segregation (简称CQRS)模式是一种架构体系模式,能够使改变模型的状态的命令和模型状态的查询实现 ...

  8. ASP.NET Core Web API下事件驱动型架构的实现(四):CQRS架构中聚合与聚合根的实现

    在前面两篇文章中,我详细介绍了基本事件系统的实现,包括事件派发和订阅.通过事件处理器执行上下文来解决对象生命周期问题,以及一个基于RabbitMQ的事件总线的实现.接下来对于事件驱动型架构的讨论,就需 ...

  9. NET Core Web API下事件驱动型架构CQRS架构中聚合与聚合根的实现

    NET Core Web API下事件驱动型架构在前面两篇文章中,我详细介绍了基本事件系统的实现,包括事件派发和订阅.通过事件处理器执行上下文来解决对象生命周期问题,以及一个基于RabbitMQ的事件 ...

随机推荐

  1. 配置vim之插件

    涉及linux平台下ctags, taglist插件 ctags是一个用于产生代码索引文件的插件,它产生的索引可以帮助我们更快的定位到特定位置. ctags支持很多语言,比如java, c, c++, ...

  2. ie6兼容性,还需要测试么?迷茫。。。

    最近公司网站在谷歌,火狐上测试都没有问题,但是在ietest,ie6上出现兼容问题 ,由于ietest好几次打开ie6都报错(尝试卸载重新安装几次无果),下载virtualbox安装自带ie6的xp系 ...

  3. BZOJ 2440: [中山市选2011]完全平方数( 二分答案 + 容斥原理 + 莫比乌斯函数 )

    先二分答案m,<=m的有m-∑(m/pi*pi)+∑(m/pi*pi*pj*pj)-……个符合题意的(容斥原理), 容斥系数就是莫比乌斯函数μ(预处理)... ----------------- ...

  4. 操作百度API

    string json = ""; try { //虽然两者都是异步请求事件,但是WebClient是基于事件的异步,而HttpWebRequst是基于代理的异步编程 WebCli ...

  5. nodebeginer

    最近对node开始感兴趣,知乎上朴灵推荐入门书籍,goddy翻译的node beginner. 貌似大家对深入浅出node.js评价都不错,以后可以考虑入手看看. 一口气看完了node beginne ...

  6. Symfony框架系列----1.入门安装

    一.安装    (1)Composer安装(可选) $ curl -s https://getcomposer.org/installer | php $ php composer.phar crea ...

  7. object-c 内存管理机制的学习

    1.内存的创建和释放 让我们以Object-c世界中最最简单的申请内存方式展开,谈谈关于一个对象的生命周期.首先创建一个对象: //“ClassName”是任何你想写的类名,比如NSString NS ...

  8. 怪兽z主机 驱动集

    这里给买家朋友送上我们主机的驱动包. 1.主板驱动.  访问密码 f334 http://yunpan.cn/QNTGxehcnLBW5 2.AMD显卡催化剂 amd catalyst(没装的话,无法 ...

  9. delphi 编译生成ipa文件

    找IPA文件 开发模式ipa文件和发布模式ipa文件,路径不同. http://www.itnose.net/detail/6101808.html 一.开发模式Development 不需要真机,可 ...

  10. VC中判断指定窗口是否被其他窗口遮挡

    本来是想判断当前窗口是否在最前面,无奈办法用尽就是不行,于是想换个思路:判断指定窗口是否被其他窗口遮挡.然后掘网三尺,找到了这个: bool CTestTray2Dlg::IsCoveredByOth ...