一种简单的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 网关将各系统对外暴露的服务聚合起来,所有要调用 ...
随机推荐
- Unity3d学习 预设体(prefab)的一些理解
之前一直在想如果要在Unity3d上创建很多个具有相同结构的对象,是如何做的,后来查了相关资料发现预设体可以解决这个问题! 预设体的概念: 组件的集合体 , 预制物体可以实例化成游戏对象. 创建预设体 ...
- JavaScript 中的数据类型
Javascript中的数据类型有以下几种情况: 基本类型:string,number,boolean 特殊类型:undefined,null 引用类型:Object,Function,Date,Ar ...
- 逆天Kali带你游遍大江南北~安全之前人铺路!
0.Linux基础学习(基本指令) http://www.cnblogs.com/dunitian/p/4822807.html 1.Kali安装到移动硬盘或者U盘中~Linux系列通用方法(包括An ...
- C# Excel导入、导出【源码下载】
本篇主要介绍C#的Excel导入.导出. 目录 1. 介绍:描述第三方类库NPOI以及Excel结构 2. Excel导入:介绍C#如何调用NPOI进行Excel导入,包含:流程图.NOPI以及C#代 ...
- <译>通过PowerShell工具跨多台服务器执行SQL脚本
有时候,当我们并没有合适的第三方工具(大部分需要付费)去管理多台数据库服务器,那么如何做最省力.省心呢?!Powershell一个强大的工具,可以很方便帮到我们处理日常的数据库维护工作 .简单的几步搞 ...
- AFNetworking 3.0 源码解读(九)之 AFNetworkActivityIndicatorManager
让我们的APP像艺术品一样优雅,开发工程师更像是一名匠人,不仅需要精湛的技艺,而且要有一颗匠心. 前言 AFNetworkActivityIndicatorManager 是对状态栏中网络激活那个小控 ...
- 代码的坏味道(20)——过度耦合的消息链(Message Chains)
坏味道--过度耦合的消息链(Message Chains) 特征 消息链的形式类似于:obj.getA().getB().getC(). 问题原因 如果你看到用户向一个对象请求另一个对象,然后再向后者 ...
- 电信计费业务:预后融合OCS到底应该实扣还是虚扣?
引入OCS的初衷之一是为了让计费系统能够参与到用户的通讯控制中来,也就是所谓的实时信控.用户在没有余额时,通讯就会被停止,不会造成"天价欠费 ",一方面保障用户的利益,一方面也保障 ...
- SMBus set up a 2-byte EEPROM address for read/write
Sequencer Engine spec: http://www.analog.com/media/en/technical-documentation/data-sheets/ADM1260.pd ...
- 深入学习HBase架构原理
HBase定义 HBase 是一个高可靠.高性能.面向列.可伸缩的分布式存储系统,利用Hbase技术可在廉价PC Server上搭建 大规模结构化存储集群. HBase 是Google Bigtabl ...