04.DDD与CQRS

时间:2024-11-01 07:09:21

学习视频来源:DDD独家秘籍视频合集 https://space.bilibili.com/24690212/channel/collectiondetail?sid=1940048&ctype=0

文章目录

  • 定义
  • 职责分离
  • DDD与CQRS的关系
  • 领域模型和查询模型特点
    • 命令场景的领域模型
    • 查询场景的查询模型
  • 架构方案
    • 领域事件
    • 方案1:标准架构
    • 方案2:同事务存储分离
    • 方案3:共享存储
  • 什么时候采用CQRS
  • 采用CQRS的哪种架构方案

定义

CQRS全称Command Query Responsibility Segregation,即命令和查询职责分离。它的设计理念来源于单一职责原则。软件系统功能拆解成命令和查询两类:

  • 命令:有副作用,会导致系统发生变更。
  • 查询:无副作用,不会导致系统发生变更。

职责分离

在架构上,把命令和查询两类功能代码分离, 分为命令模块和代码模块。

  • 一般查询模块依赖命令模块。
  • 查询模块负责生成查询类数据。比如组装一些数据,放到查询数据库里,注意这里是会往数据库里写数据的,查询模块不全是读操作。
  • 查询模块和命令模块的数据在存储上可以共享,也可以分离,甚至异构。共享:比如用同一张表,命令模块用其中的几个字段,查询模块用其中的几个字段。分离:命令模块用一个数据库或表,查询模块用另外一个数据库或表。异构:比如命令模块用关系型数据库,查询模块用非关系型数据库,文档数据库
  • 这样架构的主要目的是应对高性能查询和复杂查询。
  • 不使用DDD可以使用CQRS。CQRS作为一种架构模式,并不一定要给DDD用,DDD可以使用CQRS作为一种架构方案。

DDD与CQRS的关系

DDD可以使用CQRS作为它的一种架构实现。命令模块用DDD实现,查询模块不用DDD实现。 为什么要这样做呢?因为这样做可以降低领域模型设计难度,不然同时适配命令和查询两类业务的领域模型非常难以设计。

领域模型和查询模型特点

命令场景的领域模型

  • 对象少
  • 对象建形成树形或图结构
  • 避免冗余数据
  • ACID很重要

查询场景的查询模型

  • 对象多。如批量查询,查询最近一周的订单
  • 对象间只有查询用的关联关系。如取商品数据、交易数据,组装成购买记录
  • 大量冗余,为了提升性能。同一份数据可以在这个模型存一份,在那个模型也存一份。为的就是查询方便提高性能。或者有一些过滤条件,需要把这些条件字段也存下来,作为索引,提高查询性能。
  • ACID不重要

架构方案

领域事件

领域事件是一种领域模型,代表领域中已发生的确定的有意义事件,明确表示发生了什么事情,有意义,有人关注。它不可变,且按时间有序。

方案1:标准架构

在这里插入图片描述
在这里插入图片描述

保证消息的可靠送达,有且只有一次,不能重复消费,也不能不消费。发送事件和插入事件日志在同一个事务内,保证原子性。等到事务提交之后,才会触发事件发送。从数据库中取出事件日志,向MQ发消息,如果发送成功,会标记为已发送。如果发送失败了,会有定时任务补偿。

方案2:同事务存储分离

在这里插入图片描述
这里的存储分离指用同一个数据库不同表。如果用的是不同的数据库,那就会出现分布式事务的问题,这样就和方案1没有太大差别。

方案3:共享存储

在这里插入图片描述
在这里,已经抛弃领域事件了,同事务同表。当把命令模型持久化的时候,同时也把查询模型创建出来。

什么时候采用CQRS

不用CQRS就会污染领域模型的时候
复杂查询

  • 条件复杂,需要冗余数据
  • 搜索

高性能查询

  • 需要查询专用数据库。比如查询用的数据库有更好性能
  • 异构查询存储(关系数据库+缓存+NoSql)

只要领域复杂点,一般都需要CQRS!

采用CQRS的哪种架构方案

  • 同事务同表
  • 同事务不同表
  • 消息集成不同数据库

从简单做起,能落地能解决问题最重要! 一开始用同事务同表,这种最简单。随便场景越来越复杂,用共享的存储不能解决问题了,那就需要用同事务不同表。或者需要异构数据库存储,就要用消息集成不同设计。