广告
返回顶部
首页 > 资讯 > 数据库 >DDD里面的CQRS是什么
  • 124
分享到

DDD里面的CQRS是什么

2024-04-02 19:04:59 124人浏览 八月长安
摘要

这篇文章主要介绍“DDD里面的CQRS是什么”,在日常操作中,相信很多人在DDD里面的CQRS是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”DDD里面的CQRS是什么”

这篇文章主要介绍“DDD里面的CQRS是什么”,在日常操作中,相信很多人在DDD里面的CQRS是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”DDD里面的CQRS是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

开篇

随着业务不断发展,软件系统的架构也越来越复杂,但无论多复杂的业务最终在系统中实现的时候,无非是读写操作。用户根据业务规则写入商业数据,再根据查询规则获取想要的结果。通常而言我们会讲这些读写的数据放到一个数据库中保存,通过一套模型对其进行读写操作。而在大型系统中往往查询操作远远多于写入操作,于是就有了读写分离的思想,将读操作和写操作的模型分开定义并且提供不同的通道供用户使用。CQRS(Command-Query  Responsibility Segregation) 就是基于这一思想提供的一种模式读写分离的模式,今天就围绕着它给大家讲述以下内容:

  • CQRS的演变和架构

  • Event Sourcing 原理与应用

  • Event Sourcing 与CQRS的完美结合

  • CQRS的例子

CQRS的演变和架构

CQRS(Command-Query Responsibility Segregation)  是一种读写分离的模式,从字面意思上理解Command是命令的意思,其代表写入操作;Query是查询的意思,代表的查询操作,这种模式的主要思想是将数据的写入操作和查询操作分开。

它源于Bertrand  Mayer设计的命令查询分离(CQS)原理。CQS声明一个类只能有两种方法:改变状态并返回void的方法和返回状态的方法。而Greg Young  是负责命名这种模式为CQRS 并推广它的人。

首先来看看在没有CQRS之前是如何处理系统中的修改和查询的吧,如图1所示:

DDD里面的CQRS是什么

图1 传统的系统请求

传统的系统请求从最左边的Client开始,沿着红线往右通过Application Service对系统进行请求。这里Application Service  可以理解为系统的门面,或者是Controller层负责接收客户端的请求,此时请求的内容比较简单基本和数据库中的信息一致,因此这里使用DTO(Data  Transfer Object)直接请求。DTO经过Domain Model  以后直接到达Database,从而沿着蓝色的线条返回给Client端。传统的请求方式部分读操作和写操作,都使用同样的数据模型和一套Domain  Model以及相同的数据库。

从传统操作来看Client的请求在经过Application Service,用户意图全部被分解为CRUD操作,但是在Domain  Model中是无法体现的。为保证DTO的完整性和一致性,与操作无关的信息会被纳入DTO,查询操作和创建操作都共用一个DTO,而领域模型的业务流程被弱化。为了适应同时适应查询和创建操作,DTO被设计的面面俱到,也就显得臃肿。从而在传输中存在不必要的字段传递。

而且一次操作,在DTO与领域对象间进行多次转换,增加了系统复杂度。还有,读写操作将围绕同一数据模型展开,对于读多写少的系统而言效率并不是最高的,特别在读操作为主的高并发系统中缺点就尤为突出。

正因为传统系统架构存在上面这些问题,因此CQRS根据读写职责的不同,把领域模型切分为Command端与Query端两个部分,如图2所示,红色线部分就是Command端,其对应的是Domain  Model 对其发送Command 操作的指令往数据写入状态信息。

Query端作为查询操作,由蓝色的线表示,通过Query  Model向数据库获取信息,通过黑色向左的先返回结果给Client。Command端与Query端都通过Application Service  进入系统,共享同一个数据库,但Command端只写入状态,Query端只读取状态。

DDD里面的CQRS是什么

图2 CQRS 分为Command 端和 Query端

目前而言已经将读写操作分开了,由于两个操作依旧共用一个数据库,为了提高读写效率数据库的分离就成为必然的选择。如图3所示,于是将原来的Database,分离为Writer  Database 和Reader Database分别用于写操作和读操作。为了保证读写操作的数据一致性,需要在两个数据库之间进行数据同步。

由于数据同步是由时效性的,因此写入方是Command端,读取方是Query端,因此系统智能保证最终一致性。那么如何保证两个库之间的同步呢?下面需要引入Event  Sourcing的概念。

Event Sourcing 原理与应用

Event Sourcing也叫事件溯源,是Martin  Fowler提出的一种架构模式。其设计思想是系统中的业务都由事件驱动来完成。系统中记录的是一个个事件,由这些事件体现信息的状态。业务数据可以是事件产生的视图,不一定要保存到数据库中。

为了便于理解Event Sourcing 我们通过一个例子来进一步解释,如图3 所示:

DDD里面的CQRS是什么

图3 Command 端和 Query端 读写数据库的分离

我们从左往右看。对于一个业务类“账户”,拥有“属性”包括“账户ID”和“账户金额”信息,同时拥有“方法”包括“创建账户”、“存现金”和“取现金”。中间绿色的事件序列,是针对“账户”进行的一些列操作,按照其中的序列号来看。

1. 创建了一个银行账户,假设此时的账户ID为“0001”。

2. 针对“0001”这个账户存入300元现金。

3. 然后从“0001”这个账户取出100元现金。

4. 最后,再存入200元。

上面生成的这一系列事件会保存到下方的Event  Store的事件库中,这里并不会保存“账户”的状态信息。当需要获取“账户”数据的时候,会通过这些事件信息,还原成“账户”的最终状态,也就是“账户ID”为“0001”,“账户金额”为400。其具体实现方式是,通过账户相关的四个事件对应的处理方法,重新生成当前状态。如果每次查询状态信息都需要这样处理势必会造成资源的浪费,因此在右侧黄色的部分,我们将最终的“账户”信息通过视图的方式保存下来,以供查询。

DDD里面的CQRS是什么

图3 Event Sourcing 实例图

上面这个“账户”处理的过程,就是Event  Sourcing,说白了就是通过事件的处理模式。它将系统中的操作都按照事件的方式记录并保存,任何实体的最终状态都是通过事件的叠加和还原确认的。

Event Sourcing 包含的内容

上面介绍了Event Sourcing 的执行原理和基本概念,这里一起来看看其包含的主要内容,便于我们对它有更加全面的理解。

聚合对象:图3的例子中“账户”就是一个聚合对象,它里面包含“账户ID”、“账户金额”等的基本信息,也包含了对账户操作的方法:“创建账户”、“存现金”、“取现金”。同时“账户”在领域驱动开发中对应的是一个领域模型。

  • Event Store:在Event Sourcing模式中,事件所保存的数据库称为Event  Store。在事件中需要包含聚合对象的ID,以及事件的顺序。这样在查询的时候可以根据聚合ID从数据库中找到相关的事件,并通过事件的序号还原执行顺序。也就是事件的重现,也就是某一时刻执行的事件取出来,调用他的处理函数,还原那个时间点的业务状态。

  • 为了获取最新的“账户”状态信息,需要通过Event Sourcing  中获取对应的事件进行回放,从而获取当前的状态,这样的操作会浪费很多资源。因此我们会将聚合对象的最新数据状态,写到一个表中,这个表就是视图。又或者将这个状态信息发送给其他的应用程序进行后续的业务操作。

  • 查询的内容是针对“账户”最终状态的,因此针对的对象应该是视图。这里的设定刚好的CQRS中的读写分离不谋而合,通过Event Store存放Command  端的Event 信息,通过视图存放实体最终状态的信息,而Query 端从视图查询数据返回给用户。

Event Sourcing 的优缺点

上面介绍了Event Sourcing的原理和内容以后再来看看它的优缺点。

Event Sourcing 的优点:

  • 溯源事件与重现操作:特别是在业务复杂的系统中,一个事务包含多个操作,它们有的是并行有的串行,如果需要了解操作的执行就需要对每个事件了如指掌。Event  Sourcing 恰恰提供了事件的历史信息,方便查找任何时间点发生的事情。

  • 追踪和修复Bug:可以通过事件分析业务的执行过程,帮助发现Bug,例如重方Bug产生时的事件序列,从而定位Bug所处位置。发现Bug并且修复以后,可以通过重新聚合业务数据,重放执行的事件序列验证修复结果,同时将Bug造成的损失进行挽回。

  • 提高性能:Event  Sourcing模式下,由于是记录事件执行的序列,因此都是新增操作,没有更新操作,相对于需要更新操作的系统而言记录数据的性能是提高了。如果使用视图的方式将实体的最终状态可以传递给其他的应用,而不用写入数据库以后再读取,这种做法也提高了效率。

Event Sourcing 的缺点:

  • 转变思路:Event  Sourcing的落地需要在设计时就用领域驱动的方式开展,需要有基于事件的响应式编程思维。这种方式需要以领域模型设计优先,而不是传统的数据库设计优先。

  • 变更事件结构:随着业务流程的变化需要不断调整事件结构,对事件添加或者修改一些数据。这种行为会影响到“历史重现”,需要考虑兼容之前的事件结构。

  • 处理幂等事件:如果对应的事务在执行过程中被中断,需要通过事件回放的方式达到事务的最终一致性问题。此时需要对事件的幂等性提出要求,也就是同一个事件运行多次得到的结果不变。需要在事件处理时丢弃重复事件。

  • 查询事件数据库(event  store):由于数据库中存放的一个个事件,如果针对实体状态的查询会相对困难。需要将这些事件重放,获取最新的实体状态的信息。这也是为什么需要通过CQRS的方式将读写进行分离,Command端使用Event  Sourcing 而Query端使用Event Sourcing 发出Event 的最终状态进行查询的原因。

CQRS与Event Sourcing的 完美结合

通过上面对Event Sourcing 的介绍,可以发现它针对Event 进行记录存放到Event  Store中,并且把最终的状态放到视图中进行保存可以供给Query端进行查询。这种模式天生与CQRS就有默契的配合。

从CQRS模式的结构看,实体状态的变化发生在Command端,Command端知道业务处理进行了哪些具体操作,将这些具体的操作进行封装就形成了Event。

而Query端,查询返回的是实体当前状态状态。根据“当前状态 + 变化 =  新的状态”,如果能从Command端得到“变化”,再加上Query端自身获取的“当前状态”就能得到变化后的“新的状态”。

此时Command  端发出的Event正好符合这个“变化”,如果当变化发生也就是新Event产生时,由Command端将这个Event推送到Query端,Query端根据Event刷新状态,就能保证两端实体状态一致,达到最终一致性,如图4所示:

DDD里面的CQRS是什么

图4 Event Sourcing 和 CQRS 结合

在图3的基础上加入Event Handler 也就是图中蓝色部分,这部分接收从Domain  Model中发过来的Event信息,也就是最新的实体修改信息。再将这个信息存放到Reader Database(也可以理解为视图)中,这样新的Event  信息加上当前的实体信息就时最新的实体信息了。而采用这种方式以后Query 端依旧可以通过Reader  Database获取数据对其原来的操作并没有产生影响。

再回到Command端,其对应的多次操作的Event 会存放到Event Store中,作为业务跟踪的记录被保存下来。

上面提到的只是一种系统架构的模式,在实际运用中可以根据具体情况进行改进和优化。如图5所示,可以在Command 端和Query 端进行Event  交换的时候加入队列,满足两套应用程序部署在不同进程的场景需求。

DDD里面的CQRS是什么

图5 Command 端和Query 端加入队列

一个CQRS的例子

上面聊到了CQRS与Event Sourcing的完美结合,这里通过一个例子给大家进一步介绍其运作的过程。这个例子的背景是,对于用户(User)  而言保存了对应的联系方式(Contact)和住址(Address)。

Command 用来建立(Create)用户( User) 和更新(Update)用户(User);Query  用来查询用户(User)对应的住址(Address)和联系方式(Contact)。

如图4所示,Client 请求应用分为上线两条线,分别用四种颜色代表。我们根据不同颜色来讲解Command 端和Query 端执行的过程。

DDD里面的CQRS是什么

图4 Event Sourcing 和 CQRS 结合

红色向左的线:这里主要是针对User 的create 和update  操作,分别填充CreateUserCommand类和UpdateUserCommand类,作为UserAggregate聚合类的输入参数。在UserAggregate中分别由,handleCreateUserCommand和handleUpdateUserCommand两个方法处理,最后通过UserWriteRepository来保存到Write  database中。

  • 绿色向下的线:其连接了紫色的区域是UserProjection,它的作用是将Write database的数据同步到Read database中。

  • 蓝色向右的线:Client  发起Query请求通过AddressByRegionQuery类和ContactByTypeQuery类构建请求,将其传送到UserProjection类进行处理,其中handle方法分别对两类参数的请求进行处理。最后通过UserReadRepository获取Read  database中的信息。

  • 紫色向左的线:当从Read database 中获取信息以后,返回给Client。

DDD里面的CQRS是什么

图6 CQRS 例子图解

在了解了整体架构以后再来看看具体实现的类结构。

如图7 所示,User实体类包括如下几个字段,也就是我们要操作的业务实体。包括用户的基本信息,其中contact 和address  类的具体信息在这里不展开描述。

DDD里面的CQRS是什么

图7 User 实体类

Command  的类信息如图8所示,其内容相对简单。针对CreateUserCommand主要用于创建用户,包括UserID和FirstName以及LastName。

DDD里面的CQRS是什么

图8 CreateUserCommand 类

如图9所示,UpdateUserCommand中加入了地址和联系方式的更新内容。

DDD里面的CQRS是什么

图9 UpdateUserCommand 类

有了Command  再来看看聚合类UserAggregate,由于其中包括Create和Update的处理方法,这里介绍其中的handleCreateUserCommand方法,也就是处理新建用户命令。

这里会创建一个UserCreatedEvent对象,并将其通过WriteRepository保存到Write  database中。也就是在ES中的Event store,同时会将event 的list返回。

DDD里面的CQRS是什么

图10 handleCreateUserCommand 类

在处理完Command  以后会返回Event,这个Event在保存到数据库中的同时,也会发送和Query端作为最新的实体状态进行更新,这里会用到UserProjector类完成映射。如图11,所示,其中的project方法会针对UserID的events进行逐一处理。

DDD里面的CQRS是什么

图11 UserProjector 类

看完了Command 端和 同步的Projector,再来看看Query端的类。如图12  所示,AddressByRegionQuery类定义了UserID和State信息。

DDD里面的CQRS是什么

图12 AddressByRegionQuery 类

如图13 所示,ContactByTypeQuery定义了UserID和ContactType的信息。

DDD里面的CQRS是什么

图13 ContactByTypeQuery 类

如图14所示,上面提到的AddressByRegionQuery和ContactByTypeQuery作为参数传入到UserProjection类的handle方法中,并且返回对应的Contact和Address信息。使用了UserReadRepositiory从Read  database中获取数据。

DDD里面的CQRS是什么

图14 UserProjection

最后,再来看看测试代码这里将其分为7个步骤,如图15所示。

随机生成用户ID。

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2. 通过CreateUserCommand,创建新建用户的Command,并且通过UserAggregate生成对应的事件。

  3. 通过UserProjector将事件映射到Query端的数据库中。

  4. 通过UpdateUserCommand,创建更新地址信息的Command,生成对应的事件。

  5. 通过UserProjector将事件映射到Query端的数据库中。

  6. 通过AddressByRegionQuery,创建查询地址信息的Query。

  7. 执行查询从Read database 中获取数据与假设值进行比较。

DDD里面的CQRS是什么

图15 Command 和Query的执行过程

最后来看看这些文件的目录结构,如图16所示。

DDD里面的CQRS是什么

图16 文件结构

到此,关于“DDD里面的CQRS是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

您可能感兴趣的文档:

--结束END--

本文标题: DDD里面的CQRS是什么

本文链接: https://www.lsjlt.com/news/61877.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • DDD里面的CQRS是什么
    这篇文章主要介绍“DDD里面的CQRS是什么”,在日常操作中,相信很多人在DDD里面的CQRS是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”DDD里面的CQRS是什么”...
    99+
    2022-10-18
  • php中的DDD有什么用
    本篇内容主要讲解“php中的DDD有什么用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“php中的DDD有什么用”吧!DDD 是什么(首先它不是什么)DDD 是 Domain Driven De...
    99+
    2023-06-20
  • php里面的echo是什么意思
    本文操作环境:Windows7系统、PHP7.1版、DELL G3电脑php里面的echo是什么意思?PHP里的ECHO是一种输出字符串的语法或函数,当后面直接跟随空格和字符串时是语法,如“echo 'php';”,函数使用...
    99+
    2018-02-19
    php echo
  • mysql里面字段指的是什么
    这篇文章主要介绍mysql里面字段指的是什么,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!数据库中的字段就是指表的一列。数据表中的每一行是一个“记录”,每一个记录包含这行中的所有信息...
    99+
    2022-10-18
  • php里面break的用法是什么
    在php中,break用于跳出当前的语法结构,执行下面的语句;可以在switch、for、while和do while等语句中使用,可以终止循环体的代码并立即跳出当前的循环,执行循环之后的代码。break语句可以带一个参数n,表示跳出循环的...
    99+
    2023-05-14
    php break
  • css3中scale里面的参数是什么
    这篇文章主要为大家展示了“css3中scale里面的参数是什么”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“css3中scale里面的参数是什么”这篇文章吧。 ...
    99+
    2022-10-19
  • VB里面的命令按钮是什么
    这篇文章给大家分享的是有关VB里面的命令按钮是什么的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。VB里面的命令按钮是一个称作CommandButton的控件。它可以在VB集成编程窗口的工具箱内找到。当用户选中按钮...
    99+
    2023-06-20
  • Python的isinstance在Golang里面是什么意思
    本篇内容介绍了“Python的isinstance在Golang里面是什么意思”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!我们知道,在Py...
    99+
    2023-06-15
  • axios是不是vue里面的
    小编给大家分享一下axios是不是vue里面的,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!axios不是vue里面的。axio...
    99+
    2022-10-19
  • SAP MM MIGO界面里的'Via Delivery'选项是什么
    这篇文章主要为大家分析了SAP MM MIGO界面里的'Via Delivery'选项是什么的相关知识点,内容详细易懂,操作细节合理,具有一定参考价值。如果感兴趣的话,不妨跟着跟随小编一起来看看,下面跟着小编一起深入学习“S...
    99+
    2023-06-05
  • 聊聊nodejs里面有什么
    Node.js是一种基于Google JavaScript V8引擎的开源和跨平台的JavaScript运行环境。对于那些对于Node.js还不太了解的人来说,可能会想知道Node.js里面到底有什么。首先,在Node.js里面,我们可以使...
    99+
    2023-05-14
  • 浅谈SwiftUI 里面$0是什么意思如何用
    $0表示传递给Swift闭包的第一个参数,是Swift语言的语法糖。Swift会自动为内联闭包提供速记参数名称,可使用$0,$1,$2等名称来引用闭包参数的值。 代码 impor...
    99+
    2022-05-30
    Swift $0
  • laravel门面指的是什么
    这篇文章将为大家详细讲解有关laravel门面指的是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。在laravel中,门面是为应用服务容器中的绑定类提供的一个“静态”接口。维护时能够提供更加易于测试、...
    99+
    2023-06-29
  • Python里的Self是什么
    这篇文章主要介绍“Python里的Self是什么”,在日常操作中,相信很多人在Python里的Self是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python里的Self是什么”的疑惑有所帮助!接下来...
    99+
    2023-07-06
  • this 的六项正面与侧面是什么
    这篇文章主要讲解了“this 的六项正面与侧面是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“this 的六项正面与侧面是什么”吧!一、对象方法,&qu...
    99+
    2022-10-19
  • 什么是宝塔面板?宝塔面板的作用和功能是什么?
    什么是宝塔面板?宝塔面板的作用和功能是什么?宝塔面板是一款服务器管理软件,支持Windows和Linux系统,可以通过Web端轻松管理服务器,提升运维效率,该软件内置了创建管理网站、FTP、数据库、可视化文件管理器、可视化软件管理器等等。安...
    99+
    2023-09-08
    php 服务器 数据库
  • C#面向对象编程中的里氏替换原则是什么
    这篇文章主要介绍“C#面向对象编程中的里氏替换原则是什么”,在日常操作中,相信很多人在C#面向对象编程中的里氏替换原则是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C#面向对象编程中的里氏替换原则是什么...
    99+
    2023-07-02
  • java里%是什么?
    Java是一门面向对象编程语言,是一种广泛使用的计算机编程语言,拥有跨平台、面向对象、泛型编程的特性,广泛应用于企业级Web应用开发和移动应用开发。java里%是什么?在Java中,%是取余的意思,%是取余运算符。取余除法运算中:被除数 除...
    99+
    2016-09-13
    java教程 java %
  • ​javascript的面向对象是什么
    这篇文章主要介绍“javascript的面向对象是什么”,在日常操作中,相信很多人在javascript的面向对象是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”javascript的面向对象是什么”的疑...
    99+
    2023-06-05
  • 什么是mysql的界面工具
    MySQL界面工具是用于与MySQL数据库进行交互和管理的图形用户界面应用程序,提供了更直观、易用的方式来执行数据库操作,常见MySQL界面工具有:1、phpMyAdmin,一个基于Web的MySQL管理工具;2、MySQL Workben...
    99+
    2023-08-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作