乐趣区

关于微服务:认知篇CQRS架构模式的本质

作者:京东科技 倪新明

CQRS 只是一种非常简单的模式(pattern),CQRS 自身并不是一种架构格调,和最终一致性 / 音讯 / 读写拆散 / 事件溯源 /DDD 等没有必然的分割,它最大劣势是给咱们带来更多的架构属性抉择

1 CQRS 实质

1.1 CQS:命令和查问拆散

命令和查问拆散,Command and Query Segregation,其核心思想是在任何一个对象的办法能够划分为两类

•查问:获取数据,返回查问数据,但不扭转数据状态

•命令:扭转数据状态,不返回任何数据

基于 CQS 的思维,任何一个办法都能够拆分为命令和查问两局部:

private int origin = 0;
private int add(int value)
{
    origin += value;
    return origin;
}

上述办法既扭转了数据,又返回了数据状态,如果依照 CQS 的思维,则该办法能够拆成 Command 和 Query 两局部,如下:

private void add(int value)
{origin += value;}
private int queryValue()
{return origin;}

是否严格遵循上述约定存在争议,对于命令侧是否返回数据理论业务诉求中并不一定可能齐全对立。比方:

•”出栈” 操作同时扭转栈状态和返回数据

•某些业务场景下可能会有返回业务主键的诉求,比方下单操作返回订单号

1.2 CQRS:命令和查问职责拆散

Command and Query Responsibility Segregation,即命令查问职责拆散,由 Greg Young 提出。CQRS 在 CQS 根底之上,将拆散的级别从代码办法级别扩大到对象级别。CQRS 模式的利用非常简单,如下图所示

假如咱们的服务为 OrderService,在非 CQRS 模式下同时蕴含了查问和更新服务接口:

public class OrderService {
   //  依据 id 查问订单
    Order getOrder(OrderId)
    // 查问已领取订单
    List<Order> getPayedOrders()
    // 下单
    void placeOrder(Order)
    // 勾销订单
    void cancelOrder(OrderId) 
}

利用 CQRS 模式之后的 OrderService 被拆分成了两个接口,别离承当查问和写职责:

/**
命令侧服务
*/
public class OrderService {void placeOrder(PlaceOrderCommand command)
    void cancelOrder(CancelOrderCommand command)
}
/**
 查问服务
*/
public class OrderQueryService{Order GetOrder(OrderId)
    List<Order> getPayedOrders()}

以上这种简略的拆散就是 CQRS 模式的全副了,是不是非常简单?的确,单纯的看,CQRS 确实就是这么简略。

CQRS 最大劣势就是 基于这种职责拆散能带给咱们更多的架构属性抉择

•“查问”和“命令”两侧进行独立部署以获取更好的伸缩性

•“查问”和“命令”两侧独立架构设计

•“查问”和“命令”两侧进行独立数据模型设计

基于 CQRS,咱们能够衍生出更多的架构属性,结合实际的业务场景,进行差异化的架构设计。

团队引入 CQRS 模式之后,往往不仅仅是简略的在类的职责层面对读写进行拆散,个别会采纳更为简单的利用架构格调,如下是典型的 CQRS 架构格调:

•命令侧:命令侧引入命令总线以反对对不同命令的灵便路由;突出畛域模型的利用

•查问侧:引入查问总线对查问申请进行路由;申请链路个别间接连贯到存储层,实现不同的定制化查问需要

2 CQRS 迷思

2.1 数据模型是否要拆散

CQRS 强调命令和查问的职责拆散,但在底层的数据模型层面,CQRS 并没有进行强制限定,即 采纳 CQRS 模式并没有要求必须要进行数据模型的拆散。是否要进行模型拆散开发人员须要具体情况具体分析。

•拆散模型:查问侧和写侧模型不相互烦扰,各自在应用层的实现复杂度比拟低。但因为模型的拆散,命令侧和查问侧的数据一致性须要纳入思考范畴

•不拆散:不须要思考数据一致性问题,但因为查问侧和写侧对模型的诉求可能不统一,模型的设计往往须要折衷思考。

2.2 CQRS 和 音讯模式

CQRS 和音讯模式没有必然联系,落地 CQRS 并不一定须要应用音讯模式

如果咱们采纳了 CQRS 模式,然而命令和查问两侧底层所依赖的数据模型并未拆散,而是基于共享的数据存储和数据模型,命令和查问之间不须要额定的交互,命令侧的数据更新对查问侧实时可见。在这种架构模式下,两侧基于共享的数据曾经人造的集成在一起,不须要额定机制进行通信,天然也无需引入音讯了。如果咱们采纳 CQRS 模式,并且命令和查问两侧进行了数据模型的拆散,二者各自依赖独立的数据模型。同时,数据存储也离开部署。命令侧负责数据的更新,而查问侧只负责数据的查问,如何将数据的更新及时同步到查问侧是须要解决的问题。在这种架构模式下,应用音讯模式作为两侧的通信机制是个不错的抉择,当然,这并不是惟一的选项。

2.3 CQRS 和 ES(Event Sourcing, 事件溯源)

ES 并不是一个新的概念,在最早的金融零碎中就曾经利用。要理解 ES,咱们须要先看看传统的数据存储。在传统利用中,数据库例如 MySQL(假如存储介质是数据库,)中存储的始终是数据的最新的状态。例如咱们对某条用户的信息进行了屡次的批改或编辑,而后保留将数据存储到数据库中。无论何时,数据库中都会记录最初的、最新的用户状态。咱们只有依据 id 或其余信息查询数据库中相应的记录就能获取该用户的最新信息。这是利用中典型的数据存储特点。

当然,咱们能够基于特定的数据模型设计以保留数据的更改记录。

这种数据存储模式的特点是简略,不须要额定的保护简单的设计,咱们可能非常容易的获取最新的用户信息。然而可怜的是,咱们失落了历史信息,包含用户的用意信息。而这些信息则有助于咱们进行数据回滚、用户行为剖析以及开发过程中的调试等等。

在 ES 模式下,数据库中存储的不在是数据最新状态,而是数据的变更记录,更官网的说法是“事件(Event)”。数据库中存储的数据变动的事件流。咱们基于事件流能够对最新状态进行重建,同时也能够便捷的重现任何历史节点数据。ES 须要解决大量事件的存储和高效的实例重建问题,后续独自的文章再介绍 ES。

2.4 CQRS 和 Eventual Consistency(最终一致性)

最终一致性也经常在服务之间引入,最终一致性的目标是为了进步扩展性和可用性。

CQRS 和最终一致性同样没有必然的分割。往往采纳 CQRS 后,查问和命令两侧会采纳独立的数据模型,在这种架构模式下,命令侧的数据变动后及时同步到查问侧,两侧数据并非实时,在肯定的延时后两侧数据最终达成统一。

3 结语

CQRS 的最大劣势在于 通过将命令和查问的职责拆散,为架构师提供了更多的架构属性抉择,咱们能够在查问侧和命令侧进行独立的架构设计。对象级别的职责拆散就是 CQRS 的全副了,但在实践中涌现出了很多更为灵便也更为简单的架构格调,比方总线的引入、数据模型的拆散、一致性报这个策略、事件溯源等等。额定的组件或技术的引入必然导致复杂性和成本上升,这些选型的驳回须要团队的衡量。

退出移动版