@levinzhang
2016-12-27T21:40:21.000000Z
字数 6550
阅读 639
Posted by Andrei Kaminski on Sep 22, 2016
本文主要关注CQRS架构的业务场景,涵盖了命令查询职责分离(Command Query Responsibility Segregation)的核心理念,并与通用的N层架构进行了对比。它所带来的收益主要在于可扩展性和可维护性,如果选择CQRS架构的话,能够减少总拥有成本,并且会带来投资回报率的增长。
CQRS这个术语指的是一种软件架构模式和概念,从事领域驱动开发(Domain Driven Design)的软件工程专家最终都会碰到这个模式。但是,它背后的理念是什么呢?在有些场景下,CQRS为什么是比常见的N层软件架构更好的可选方案?这两种模式对比起来,各有什么特点?另外,更重要也真正的值得关注的是,“它到底能为业务带来什么?”我们试图在更为宏观的层面来剖析CQRS应用架构能够带来什么价值。
那么,基于CQRS的应用架构的关键点是什么呢?我们先从概念的定义开始吧。
命令查询职责分离(CQRS,Command Query Responsibility Segregation)是一种应用架构模式,它会将应用分为两部分:查询部分(查看模型)和命令部分(写入模型)。每一部分都负责处理特定的操作集——分别也就是读取类型和写入类型。CQRS概念最初是由Greg Young提出和积极倡导的。它是CQS(命令-查询分离)理念的自然延伸,CQS理念由Bertrand Meyers提出,主张将方法分为命令和查询。CQRS使用了相同的原则,不过将其扩大到了整个系统中。
按照这种架构,业务逻辑层的两个组件将会相互独立地运行。因此,读取模型(Read Model)将会处理用户的查询——在处理能力方面的要求会更少一些,而写入模型(Write Model)将会经历一个很长的处理路径,包括校验、队列、消息以及用户命令要执行的业务规则处理。
CQRS模式受到模型驱动开发(Domain Driven Design)拥护者的推崇。这种方式强调在实现应用的时候,将解决业务问题放在第一位。它关注的中心点在于业务领域及其发挥作用的上下文之间的紧密协作。它会聚焦于业务而不是技术问题,并且要找出特定领域中所有细微的内容,这之所以能够实现要归功于采用了一种通用语言(Ubiquitous language)——能够被实现团队、业务分析师、领域专家以及其他人员理解的语言。这门语言能够让所有的团队成员(业务和技术)共享工作成果,他们会定义通用的业务对象来描述解决方案的领域模型,还会定义这种模型中的特定上下文,在这一点上要取得共识。随后,这些业务对象会被代码本身来使用。
因为基于CQRS的架构允许将逻辑分为两种不同类型的任务,所以它创造了一个有利条件,能够使用这些明确的对象来编写代码并且能够强化以业务为中心的DDD方式的影响力。技术团队更加关注于业务规则和处理流程,而不是基础设施或者不同类型的架构所带来的代码冲突。鉴于这一点,CQRS和领域驱动设计通常会一拍即合。
在Web中,最常用的一种架构就是典型的多层架构。在它的一个简化版本中,应用会分为三层:用户界面、业务逻辑和数据库层。在这种架构中,查询(读取系统状态的请求/要展现某些数据的请求)和命令(改变系统状态的请求/写入新数据或对数据进行变更)会在相同的业务逻辑层来进行处理。
如果有人想要按照路径最短且最安全的方式从A到达B的话,那么他应该不会尝试既漫长又乏味的路线。类似的,如果一个Web解决方案就是相对静态的品牌展示功能,几乎没有用户的交互性输入(假设只有类似于简单的联系人表单这样的东西),那么我们肯定不希望采用过于复杂和成本高昂的系统。但是,随着更多的业务规则和交互点添加到应用逻辑之中,解决方案就不会那么简单了。
举例来说,我们仔细看一下典型的在线商店解决方案是如何运行的。
当用户选择了一件喜欢的商品并准备购买的时候会发生什么呢(换句话说,在电子商务系统中,当我们想要写入购买命令的时候会发生什么)?当用户点击了购买按钮,首先,需要检查所请求条目的可用性。如果系统的响应确认所需的数量能够满足,那么就会邀请用户填写订单表单并提供派送地址、账单信息、支付信息以及其他完成购买行为所需的详细信息。然后,用户点击支付按钮,系统将会运行所有的校验过程(派送地址校验、销售税校验、账单地址校验、信用卡信息校验以及余额校验等)。当这些过程成功完成之后,用户会看到一个确认信息,包含了订单的细节和后续操作的指南,并且会对系统状态进行相应的变更(更新仓库产品的可用状态,注册新的购买订单并将投递细节发送给仓储团队等)。
现在,我们看一下如果不采取这种方式的话,用户还可以怎样与在线商店进行交互。我们的用户可以向电子商务系统发送什么样的读取请求呢?在线商店的访问者在尝试使用系统的时候,可以点击某个商品条目来查看其特征的详细描述,仔细查看可能会购买的商品。在这种情况下,访问者将会发送对单个商品页面的读取请求,这个页面将会展现给访问者。用户还可以搜索我们的商店,查找感兴趣购买的商品或者得到它的更多信息。例如,如果在线商店是专门销售移动电话的,那么有些用户在访问我们的电子商务方案时,可能会根据特定移动设备的型号进行搜索。对于这种请求,我们的系统应该为UI返回一个搜索结果页面,结果中包含了匹配用户搜索的信息。最后,有些用户可能还会根据特定的参数如设备制造商,来查找分类后的商品条目,或者是使用匹配其断言(如屏幕分辨率、内存容量、大小、外观和体验等)的过滤器来对商店进行搜索。在上述的所有情况中,我们都需要处理传入的查询、搜索我们解决方案的数据库并生成相关的UI视图,在这个生成的视图中需要包含用户所请求的信息。
在N层结构的解决方案中,购买商品的行为会在逻辑层遵循相同的路径,这与用户查询各种条目并展现在UI上相同。但是,购买命令将会进行商品的可用性检查和各种类型的校验,而在线商店的查询命令会触发的过程包括元数据搜索、过滤数据存储中的信息并组合相关的UI视图。
我们可以看到,在通用的N层架构中,不同类型的操作会不均衡地经过相同的应用层。
如果某家公司非常满足于它在行业中的现状,那么这种类型的架构对它非常合适。如果应用的用户不会随着时间推移而变化,如果要处理的信息量始终维持在相同的水准,如果应用不会经历高负荷的时间段或非典型的流量峰值,如果应用始终在它预先定义好的容量范围内运行,那么多层应用的架构几乎不会遇到任何的问题。但是,在现实生活中,我们遇到这种场景的频率又有多高呢?
在现实生活中,在应用发布之后的一段时间内,如果应用有发展推动力的话,那么它的性能就会经历一定的考验。随着用户数量的增长,要维持相同等级的可用性和故障恢复标准,就需要更多的系统资源。
在这种情况下,业务管理者就会开始考虑提升已有系统的性能。
但是,如果像N层应用这样,查询和命令使用同一个业务逻辑层的话,那么系统从一开始就会承受不必要的压力,当请求的数量不平衡,更多数量的请求都是某一种类型时(通常来讲,更多的都是简单的读取),这一点尤为明显。在这种情况下,可用资源的使用效率低下,因为系统会选择一个很长的路径,而不是更合理的捷径。
其次,对于系统的未来发展而言,N层架构代表了一种更复杂的方式。如果系统的负载因为某种类型的操作增加(假设查询的数量出现了增长),出现不均衡的增长时,我们并没有办法按照期望的方式扩展系统的规模。在多层方案中,开发团队如果想针对特定类型操作的业务逻辑进行细粒度调优的话,这是很难实现的。我们必须提升整个应用层的处理能力,才能确保性能能够达到用户的期望。采用这种方式来扩展系统的话,将会导致一些操作在系统中会执行很长的路径,而不是从A到B的捷径。这同样会导致系统资源的低效分配。
上面提到这些方面恰好是CQRS优于传统N层架构的地方。
我们放弃熟悉和可靠的架构,转而选择一种看似混乱且复杂的架构,对此所有的质疑都是情有可原的。但是,假设我们要着手创建一个高级的Web解决方案,这个方案有着复杂的协作上下文环境,在写入模型中有多种命令类型,同时还要为多并发用户提供高质量的在线服务,那么我们仔细权衡一下N层应用架构的上述瓶颈,也就是性能、可扩展性以及效率,然后考虑一下基于CQRS的架构所能带来的收益:
它适用于所有的地方吗?CQRS是针对所有软件开发任务的银弹吗?当然不是!对于一些技术驱动的业务解决方案来说,这种模式特别合适,但是在其他的一些场景下,则是完全不推荐使用它的。那么,在什么场景下不适合使用CQRS呢?如果整个软件系统,你想从头到尾都想采取CQRS的话,那这就是不合适的。
如果我们在设计复杂应用程序的时候,缺乏模块化的结构,并试图将CQRS模式应用到整个系统之中,那么很有可能会形成一堆像迷宫一样的代码,整个代码结构都会非常怪异。首先,解决方案架构要拆分为更小的单元(边界上下文),只有在此基础上,其中的一部分或全部单元才能使用CQRS进行优化。
边界上下文(Bounded context)最初是领域驱动设计所使用的术语。简单来讲,我们会在特定的业务领域中创建边界上下文,它们对应了领域中逻辑一致的各个组成部分。在软件系统中,它们就是具有特定职责的更小的组件,通常会满足特定部门的需求(或者是部门中具有明确职责范围限制的业务单元)。每个上下文的职责不会重叠。例如,如果你有一个整个组织都会使用的工作流管理系统,它可能会包含IT部门的上下文、会计部门的上下文、销售部门的上下文、客户支持部门的上下文等等。这些上下文会有着清晰的边界,但是会共享一些理念,比如客户销售和会计上下文。因此,在复杂的系统中,如果我们能够将CQRS应用到特定模块上,而这些模块分别对应于业务领域中的上述边界上下文,那么这样的决策就是非常明智的。
在前文中,我们列出了CQRS模式的收益和局限性,对基于CQRS的实现进行适当的考虑和评估,从前瞻性的角度来看,尽管在初始的阶段会有较高的时间、资源和预算投入,但是它会带来更低的总拥有成本。我们需要对其进行更细致的观察——在这方面花些工夫是值得的。
Andrei Kaminski是Softeq企业级Web开发团队的一名.NET开发人员,并且还是微软认证的专家,主要的关注点在于为复杂的业务问题寻找解决方案,在这个过程中会使用优雅的应用架构以及技术世界所提供的最好的东西。Kaminski有多年实际的企业Web应用开发经验,包括BPM、运营智能以及业务流程自动化方案。在他的职业兴趣列表中,CQRS与事件溯源是排名第一的应用实现方案。
查看英文原文:CQRS for Enterprise Web Development: What's in it for Business?