经典的应用系统结构、CQRS与事件溯源
2017-06-24 08:01:23
【注:本文主体内容节选并翻译自Greg Young的CQRS Documents。如需查看原文,请单击此处】
对比两种不同风格的系统架构,我们发现,两者在客户端(Client)所需要投入的工作量大体上是相同的,因为两种不同架构下的客户端所做的操作大体相同:从系统获得DTO,将DTO转换为View Model并显示在UI上,接收用户输入,产生命令,然后通知应用系统做相应的操作。在查询功能的实现上,这两种系统架构所花费的成本也大体相同:在经典的应用系统结构中,查询是建立在领域模型上的,而在CQRS结构的应用系统中,查询则是另外一件事情:它由一个简单的读取访问层(Thin Read Layer)提供,而这个读取访问层会直接将数据映射在DTO上。通过对“命令与查询职责分离”的讨论,实现一个简单的读取访问层不会花费更大的成本,相反,在很多情况下会节省成本开支。
当我们深入地去了解这种架构的时候,我们会发现,当项目规模变得很大的时候,有一个非常大的瓶颈,是数据存储系统。尤其是关系型数据库。当然,虽然现在90%以上的系统采用的是关系型数据库,但是项目规模的扩大毕竟也不是一种常态,因此,这种瓶颈的存在也不会是什么太大的问题 #p#分页标题#e#项目规模
二、CQRS与事件溯源 CQRS与事件溯源有着相辅相成的关系。CQRS允许事件溯源作为领域的数据存储机制。然而,使用事件溯源的一个的缺点是,你无法向你的系统提出类似“请告诉我所有名字为Greg的用户”这样的问题,这是由于事件溯源无法提供对象的当前状态而引起的。CQRS支持的查询是:GetById - 通过ID来获得某个聚合。下图为基于CQRS的应用系统结构,可以与上图的经典结构作一个对比。 从以上几点考虑,这两种不同风格的系统架构所要完成的工作量几乎是等同的,并不存在额外的工作量,也不存在更少的工作量。这是两件完全不同的工作。在建模的时候,基于CQRS架构风格的应用系统似乎有一些额外的工作要做,因为你需要定义一系列的事件对象,同时还要编写事件处理器;但这种工作量相对而言还是比较小的,它有效地降低了甚至避免了“阻抗失衡”效应。总而言之,事实上在大多数情况下,基于CQRS与事件溯源的应用系统更节约成本,而且更加高效。 三、简单总结
上面的架构依赖于一个后台的数据存储系统。虽然通常情况下是一个关系型数据库,但这种数据存储系统可以用多种方式实现,比如采用键值存储、对象数据库或者甚至是XML文件。不管怎么样,这个数据存储系统保存的是领域模型中对象的当前状态。在数据存储系统的上层,是整个系统的核心部分:应用服务器(Application Server)。在应用服务器中,有一个称之为“Domain”的部分,它包含了整个系统的业务逻辑,比如对来自于外部请求的业务验证等逻辑。于此同时,“应用服务”也是应用服务器中的一部分,它向外界提供了访问领域模型的接口,同时在调用者与领域模型之间实现了解耦。再外层是Remote Facade,它通常可以是SOAP、定制的TCP/IP通信、通过HTTP进行传输的XML文档、TomCat,或者是输入数据的工作人员。Remote Facade的主要职责是为外部系统与应用服务器之间的交互提供远程代理和桥梁的作用。整个应用服务器的作用是对后台数据存储系统进行抽象与解耦,同时对整个系统的业务逻辑进行处理。这样的经典架构曾经非常流行,而且即便是现在,绝大部分系统仍然将其作为默认的架构风格。
现在让我们分析一下这样的经典应用系统结构。这样的架构有很多特点,当然,其中一些特点在某些应用场合表现得非常不错,而也有一些特定却无法适应其它的一些应用场合。作为架构师,我们需要善于总结和发现,使得所选架构能够地满足系统需求。
在了解基于领域驱动设计(Domain Driven Design, DDD)的应用系统之前,首先让我们了解一下经典的应用系统结构。这种经典的应用系统结构往往被认定为设计与开发分布式应用系统的一种标准化方案,下图描述了这种经典的应用系统结构。
工具化、框架化在这种经典的应用系统结构上实践领域驱动设计是不可能的事情,即使之前有不少人试图通过这种架构来实践领域驱动(daxnet:之前我也认为DDD可以在这种架构上实践,而且从实现角度来看,并没有什么是行不通的,然而Greg Young却并不认同,原因是由于通用语言,当然Greg Young也不一定是完全正确的,但他要比我们实践的更多,他的推断更具参考价值,我们还需要在实践中去体会这种差别)。其原因简单地说是这种架构中的对象模型无法正确地表述成通用语言。我们不难发现,这种经典的架构中,只有四种谓词:新建(Create)、读取(Read)、更新(Update)和删除(Delete),也是我们常说的CRUD。由于Remote Facade是面向数据的(也是面向DTO的),于是,Application Service也需要提供相同的面向数据的接口。这也意味着,领域模型也只能支持这四种谓词;而当我们跟领域专家进行交流的时候,我们往往采用的是“通用语言”,而通用语言的内容很丰富了,完全不仅仅是这四种谓词
一、经典的应用系统结构
有不少网友仍然对CQRS与事件溯源(Event Sourcing)不是很了解,对经典的应用系统架构与CQRS架构之间的差别没有一个大概的认识。本文基本上摘自Greg Young的CQRS Documents一文(由daxnet本人翻译并做了简要的注释),希望能够对应用系统架构的爱好者有所帮助。
简单
仍然不要因为感觉CQRS看上去更“先进”,去生搬硬套,这只能给你带来失败的教训。还是要根据项目的实际情况对整个系统的架构与技术选型作出准确的判断,这也是架构师的主要职责所在。虽然我的博客中大部分内容都在讨论领域驱动设计与CQRS,但这并不表示我会去抵制经典的架构风格,我也同样在经典的应用系统结构上不断地学习和思考,我只是希望能够通过博客这么一种东西,来整理一些新鲜的事物,同时也帮助软件设计与架构的爱好者们开拓视野,丰富知识。 领域驱动
仍然从成本的角度考虑,这两种系统架构的区别应该是在领域模型及其持久化的部分。在经典的架构风格中,为了将领域模型持久化到数据库,通常情况下ORM承担了大量的工作,但这同时也在领域模型与持久化机制之间产生了“阻抗失衡”,这种效应终导致在生产环境中产生了高额成本支出,同时,开发人员也需要更丰富的知识与更多的经验来处理由“阻抗失衡”导致的问题。在CQRS的架构风格中,从C部分(Command部分)来看,不会存在这种“阻抗失衡”效应。领域模型产生事件,而数据存储系统则保存这些事件,领域模型所关注的仅仅是这些事件而已。然而在Q部分(Query部分),则会产生“阻抗失衡”:事件处理器(Event Handlers)需要根据事件的具体数据来更新Query Database,这里的阻抗失衡产生于事件与数据关系模型之间。然而,Query部分的阻抗失衡效应要远小于由领域模型与关系型数据库之间产生的阻抗失衡效应,而且Query部分的阻抗失衡效应要更容易处理和解决。这是因为,事件本身是没有结构的,它仅仅表述对关系模型应该采取哪些措施。 总之,经典的应用系统结构能够适用于绝大多数的系统开发,它有着的优点,是简单。然而,它不适应于基于领域驱动设计的项目开发,如果你非要在这样的架构下实践领域驱动,那必定是失败的。
- 上一篇:溯源系统如何给力生猪质量安全?
- 下一篇:经销商溯源管理系统