所在的位置: mysql >> mysql优势 >> 了解一下CQRS模式

了解一下CQRS模式

刘军连出诊的医院 https://m.39.net/disease/a_9103325.html
背景问题简单的需求

当我们系统中的数据模型层级较少时,数据模型足够简单时,模型与数据库可以直接进行映射。这种简单数据模型使我们不需要针对其相互关系进行复杂的建模设计,直接在工程中使用经典的三层模型就足以支撑项目需求。

对于这种简单系统,过度设计会增加后续维护、重构的成本(并不能保证预设计能完美符合后续需求)。同时,对于简单系统,我们大部分的需求都只涉及其中的少量数据模型逻辑处理。

而我们直接对数据模型进行CURD就能满足需求,进而的结论就是:

针对简单需求,我们不需要特别区别查询和增删改的程序结构。

复杂的需求

如果我们的系统具有一定复杂性,这种复杂性可能是源于访问频次、数据量或者是数据模型数量。这时候我们遇到的问题是数据在查询和更新的需求差距逐渐变大。

频次:数据的查询频次会远高于新增、更新、删除频次。

数据量:数据量变大后会增加对数据进行分库分表的设计诉求,从而导致数据查询变得的复杂性(涉及分表关键字)。

数据模型数量:数据模型数量的增大,会导致在进行新增、更新与删除操作时同时影响的数据模型变多,而在查询时同时跨多模型的查询条件会让查询的性能具有极大的挑战性。

根据以上举例我们可以发现,当我们的需求具有一定的复杂性后,根据引入复杂性的不同,会导致系统功能上需要用更加复杂的设计来对需求的复杂性进行支撑。同时我们也可以发现,引入的不同复杂性在增删改和查询方面的带来的功能需求差别很大。

所以:

需求的复杂性会放大程序中查询和增删改的设计差异。

DDD的需求

如果我们对系统整体的构建与设计有了更高的可维护性与可扩展性要求,以至于我们需要使用DDD来设计整个系统。

在这种情况下往往模型中具有相对复杂的模型关系,在增删改时我们需要将所有请求封装为领域对象,以便程序可以基于领域模型完成大量复杂的校验、业务逻辑。而在查询需求时,我们常常需要组织跨领域数据来完成一个列表中数据内容的展示。所以:

在DDD设计中,增删改操作便于应用领域模型执行,而查询操作往往无法直接通过领域模型执行。

CQRS模式问题的抽象

根据第一节中的内容我们可以发现,在进行系统架构设计时,当系统出现复杂性后存在一个核心问题:

增删改类型的功能与查询类型的功能,在功能需求上具有较大的差异。

这种差异带来的直接结果就是在系统开发的过程中,针对增删改和查询操作的业务设计上差异会比较大。如果举几个例子来说的话,比如:

针对增删改系统我们需要事务来保证多领域模型的更新原子性;针对查询我们需要增加缓存来提高热点数据的查询性能。

数据读取和写入的模型通常是不匹配的,他们维护和查询的列或者属性坑没有交集。

在更新的时候查询数据可能会产生冲突。

使用统一模型进行存储可能会导致复杂查询时的性能降低。

CQRS本质

由于存在增删改与查询逻辑有差异的这个问题,为了更好的针对差异进行抽象,我们可以将它们分开进行设计。也就是我们的CQRS模式,即命令查询的责任分离CommandQueryResponsibilitySegregation模式。其中我们称增删改为命令型操作。

CQRS本质上是一种读写分离设计思想,这种框架设计模式将命令型业务和查询型业务分开单独处理。通过这种方式,CQRS可以针对命令和查询单独进行业务模型上的设计,从而用更加适合各自场景的方案与组件来提供能力。

查询

查询操作并不会修改数据库中的内容,所以查询本身是一种幂等操作,以同一个查询条件在系统不改变的情况下反复执行会返回相同的结果,我们可以针对这种特性提供数据缓存来提高系统性能;同时因为不影响数据库,查询逻辑是不会产生数据一致性问题。查询往往会存在较高的使用频率。

命令命令操作会直接修改数据库,并针对多个领域模型的情况下我们需要增加来保证操作的原子性。而对于一个命令操作,我们往往是不直接依赖命令的返回值的,所以通常可以异步执行命令操作。对于一般系统来说,往往命令操作的使用频次会较低。

简单实用

由于CQRS的本质是对于读写操作的分离,所以比较简单的CQRS的做法是:

CQ两端数据库表共享,CQ两端只是在上层代码上分离。

这种做法在不对数据库进行分离设计的情况下,CQ两端在上层代码进行分离个字单独维护,例如命令型的都用xxxManagerController、xxxManagerService来定义,而查询则直接用xxxController、xxxService定义。

因为使用同一个数据库,所以没有CQ两端的数据一致性问题。但因为已经对上层代码进行了抽离,所以可以满足一些设计特性如:

命令应基于任务,而不是以数据为中心。

命令可以放置在队列上进行异步处理,而不是同步处理。

查询从不修改数据库。查询返回的DTO不封装任何域知识。

这种方案可以满足代码逻辑上的分离维护,但由于是使用同一数据库表,所以无法根据CQ两种业务的特点单独进行模型设计。




转载请注明:http://www.aierlanlan.com/grrz/8439.html