Electronic Joint Business

Solution for E-Business

设计模式之 CQRS 模式简介

用户和信息系统交互的主要行为就是对数据库或数据仓库进行 CRUD 操作,设计者通常为此构建出一个可供创建、读取、更新和删除的数据模型。该模型的接口就是供存储和获取数据之用的。但这种标准的分层体系结构并未处理两个问题:协作(collaboration)与失效(staleness)。1 协作是指多个参与者使用/修改相同的数据集的情况 — 尽管他们未必真的是相互协作。而失效是指在协作环境中,数据已显示给用户之后,相同的数据可能已经被另一个参与者修改 — 即数据失效了。出于性能使用缓存则进一步加剧了数据失效的问题。解决这两大问题,正是命令查询责任分离模式 (CQRS — Command Query Responsibility Segregation )背后的驱动力。 在图1-1 中,我们可以看到用户基本上会发起两类操作:命令(Command) 和查询(Query),如显示当季销量最好的产品属于查询,而提交订单、修改密码等则属于命令,也就是说: 命令操作:用于修改单个对象或者整个系统的状态(有时也称为 modifiers 或 mutators). 查询操作:用于返回结果且不改变对象状态 从事务的角度上看,查询操作与命令操作对事务的要求不一样。由于查询操作不会改变系统状态,因而,不会产生最终的数据不一致。从请求响应的角度来看,查询操作常常需要同步请求,实时返回结果;命令操作则不然,因为我们并不期待命令操作必须返回结果,这就可以采用fire-and-forget方式,而这种方式正是运用异步操作的前提。 对于大多数软件系统而言,查询操作发起的频率通常要远远高于命令操作。由于查询操作不会造成数据的修改,因而它属于一种幂等操作,可以反复地发起,而不用担心会对系统造成影响。基于这种特性,我们还可以为其提供缓存,从而改善查询的性能。命令操作则与之相反,它会直接影响数据的改变。 只要充分理解了 CQRS 模式的设计意图,那么理解 CQRS 模式就容易得多,图1-2 为 CQRS 模式架构图。 在这个架构图中,最核心的概念是Command、Event。以我的理解,CQRS模式的风格源头就是基于事件的异步状态机模型。抛开命令查询分离这一核心原则,这才是CQRS的基础内容。CQRS对设计者的影响,是将领域逻辑,尤其是业务流程,皆看做是一种领域对象状态迁移的过程。这一点与REST将HTTP应用协议看做是应用状态迁移的引擎,有着异曲同工之妙。这种观点(或设计视图)引出了Command与Event的概念。Command是系统中会引起状态变化的活动,通常是一种命令语气,例如注册会议RegisterToConference。至于Event,则描述了某种事件的发生,通常是命令的结果(但并不一定是直接结果,但源头一定是因为发送了命令),例如OrderConfirmed。我发现,这种事件更接近于一种事实,即某次数据改变的结果,是一种确定无疑已经发生的事实。这一思想直接引入了Event Source,并带来Audit(审计)的好处。而它更是与Datomic数据库的设计哲学一脉相承。Datomic的设计哲学就是:“将数据(Data)看做是事实(Fact)。每个事实都是过去的痕迹,虽然这种过去可以遗忘,但却无法改变。”Event Source可以将这些事件的发生过程记录下来,使得我们可以追溯业务流程。 Command和Event都有对应的Handler来处理。它们具有一个共同的特征,即支持异步处理方式。这也是为何在架构中需要引入Command Bus和Event Bus的原因。在UI端执行命令请求,事实上就是将命令(注意,这是一个命令对象,你完全将其理解为Command模式的运用。注意,命令的命名一定要恰如其分地体现业务的意图)发送到Command Bus中。Command Bus更像是一个调停者(Mediator),在接收到Command时,会将其路由到准确的CommandHandler,由CommandHandler来处理该命令。在Axon Framework中,Command Bus提供了dispatch()方法对命令进行分发。也就是说,在它的实现中,并没有对Command提供异步处理,而仅仅是完成路由的功能。不过,在我自己的框架实现中,我却将Command Bus看做是消息通道,而将Command Handler看做是该消息通道的侦听者。因此,我引入了队列来实现Command Bus。很难说明哪种方式更合理,这还要取决于业务模型。整体来看,后一个方案似乎有些重型了。不过,我现在并未引入消息队列,而是使用了Scala的Actor,从多线程的角度来实现CommandBus的异步模型,也可以说得过去。 Event的处理与之相似。Axon Framework同时支持同步和异步方式。从框架角度讲,提供更多的选择是一件好事。但基于CQRS模式的核心思想来看,如果对Command(包括Event)的处理未采用异步模型,它就没有发挥出足够的优势,此时采用CQRS,反而会增加设计难度,有些得不偿失。 在Command端,基本的处理流程是由UI发起命令请求,发送到CommandBus,并由它分发给对应的Command Handler来处理命令。Command Handler会与领域对象,特别是与Aggregation Root对象通信。在处理了相关的业务逻辑后,会触发Event。一方面,它会将Event放到Event Store中;另一方面,同时会将Event发送到Event Bus,再由Event Handler处理事件。根据Axon […]

,

Leave a Reply

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload CAPTCHA.