其他分享
首页 > 其他分享> > CQRS架构-《复杂软件设计之道:领域驱动设计全面解析与实战》笔记 - 5

CQRS架构-《复杂软件设计之道:领域驱动设计全面解析与实战》笔记 - 5

作者:互联网

我的博客阅读本文

文章目录

1. DDD的实现架构

DDD的实现架构有很多种,这些架构都是一种关注点分离模式的实现,也是SOLID单一职责原则的体现,将人们关注的一个职责与其他职责分离,不要试图混合在一起。传统的SOA架构在这方面有很大缺陷,造成了一种单体耦合的架构,虽然这样的大型服务能够实现一定程度的复用和重用,但是在重用和解耦之间需要有一个取舍,在这两者之间如果非选择一个,那么首先选择解耦。通过解耦可以实现更小粒度的重用,虽然这种重用粒度太细小会使得提高生产效率方面的效果不是很明显,但是随着系统的扩展和复杂性的提高,其优点将逐步体现

1.1. 三层架构

常见的三层架构:表现层、应用层和数据层

Untitled

表现层是与外界进行联系,处理所有传入请求和放回响应的地方,这是MVC模式实现的地方。表现层依赖应用层来执行系统提供的所有功能。表现层仅仅依赖应用层,在React.JS或Vue.JS等浏览器富客户端流行的情况下,表现层已经在浏览器客户端实现,而传统SpringMVC则是在后端服务器实现MVC。

应用层是开发应用程序提供所有功能的地方,这也是业务逻辑所在,业务领域核心所在,也就是进行所有业务规则验证的地方。应用层仅仅依赖数据层,保存所有的数据供以后使用,或获取某些先前保存的数据,应用层将其计算结果返回表现层。

数据层,保存所有的数据供以后使用。

在这种分层下,应用层既负责业务决策,也负责业务协调,在复杂在业务变得复杂后,应用层会越来越臃肿。

优点

缺点

1.2. 传统DDD分层架构

传统DDD架构在传统三层架构基础上增加一个领域层,将数据库层或仓储层作为基础设施层,而应用层则用来协调领域层和基础设施层,因为最终一个功能是需要业务领域决策加上仓储来实现的

Untitled

1.3. 清洁(Clean)架构

清洁(Clean)架构是著名软件工程大师RobertC.Martin提出的一种架构整洁清晰之道,也是当前各种语言开发的目标架构。

Untitled

同心圆代表各种不同领域的软件。一般来说,越深入代表软件层次越高。外圆是战术实现机制,内圆是战略核心策略。

此架构能够工作的关键是依赖规则。这条规则规定源代码只能向内依赖,在最里面的部分对外面一点都不知道,也就是内部不依赖外部,而外部则依赖内部。这种依赖包含代码名称、类的函数、变量或任何其他命名软件实体。

同样,在外圆中使用的数据格式不应被内圆中使用,特别是如果这些数据格式由外面一圈的框架生成时。清洁架构不希望任何外圆的东西影响内圆的业务核心

使用清洁架构的领域模型有以下特点:

代码结构:

1.4. 六边形架构

清洁架构实际总结了六边形架构的特点,其用例和接口适配器类似与六边形架构的适配器。它们的核心都是将业务逻辑和基础设施相分离

Untitled

六边形架构的特点:

代码结构:

异步应用的六边形架构代码结构:

调用顺序 presentation → api → domain → spi → infrastructure

1.5. 垂直切片架构

垂直切片架构是来自JimmyBogard的CQRS实践总结,它是对清洁架构以及六边形架构的否定。根据这些架构的分层方法,一个业务功能会跨越这些分层执行,当需要增加或修改一个功能时会涉及在这些层内实现多次修改,JimmyBogard的想法是:如果根据功能进行分“层”(称为“片”)则会大大提高开发效率,这种“片”是垂直于分层的:

Untitled

这个思路与微服务的想法不谋而合,微服务按业务进行垂直切分,每个小组单独分担几个微服务

因为一个微服务代表一个有界上下文,垂直切片方式也被用来进行有界上下文的分类,一个垂直切片就可能是一个有界上下文。当然,垂直切片强调的功能粒度非常细腻,而且是根据不同的技术实现进行切片的,并不是根据业务能力进行切片。这种架构的总体原则也是根据SOLID原则中的单一职责,既可以按业务单一职责切分,也可以按技术实现的单一职责切分。

垂直分片架构提出了从请求的功能职责角度进行分片分层的思路,对于不同的请求功能应用不同的策略分层,如果是简单查询,可以直接使用SQL,无须跨越多个抽象层。

1.6. CQRS架构的特点

CQRS(CommandQueryResponsibilitySegregation,命令和查询职责分离)是由GregYoung提出的模式,本质上是一种读写分离的架构

分层设计虽然初衷是为了分离领域和技术,但是过于武断和粗粒度,有时一个简单的查询如果也遵循这种分层,那么维护拓展起来未免过于复杂。

从读写职责角度对请求的功能进行分类。当界面的表单数据提交到后端时,就会有写入表单数据的命令,命令送达聚合模型,将命令中的DTO提取出来,进行业务逻辑检查或计算,聚合中的状态发生改变,发出领域事件,这条路线称为Command模型路线。而另外一种请求则没有这么复杂,搜索只是从搜索库中获取搜索的结果,并没有任何复杂的业务逻辑计算,这时候如果也是使用聚合模型,可能会使得聚合模型的设计需要为搜索方面的功能需求进行添加和修改,这使得领域模型的职责变得复杂了

命令是客户端让服务器做事情,是从客户端向服务器后端发出写入操作命令,通常会改变后端模型的状态;而查询是服务器后端向客户端返回结果。这是两种不同的方向,如果这两种方向涉及的职责耦合在一起,使得领域模型的设计需要兼顾这两种方向,就容易耦合成一个大的上帝式的对象。

Untitled

CQRS源于BertrandMayer设计的命令查询分离(CQS)原理,CQS声明一个类只能有两种方法:改变状态并返回void的方法和返回状态但不改变状态的方法。

根据CQS思想,任何功能可以划分为读取/查询和命令/写入两大功能,写后再读也归为写功能。因此如果将功能粗暴简单地分为读写两种功能,开发团队也可以由此划分为两种:DDD业务逻辑实现和数据报表分析。

CQRS的实现可以分为三个步骤:

以上这三步完成任何一步都可以称为CQRS,第二步和第三步是针对大规模系统的

Untitled

1.6.1. 命令和查询分离

一般情况下,一个聚合模型的CRUD操作总是放在一个领域服务中进行传递协调,现在需要将其分为CUD命令模型服务和读取查询服务两种:

Untitled

根据数据进出的方向分为Command命令模型和Query查询模型两种。这里使用Handler而不是服务描述,突出了Handler作为专门处理命令或查询的一种方式,但是真正进行命令或查询处理的并不是Handler本身,而是它委托给领域模型或查询模型。这里的Handler也是一种命令或查询的传递处理方式,它接受来自前端的请求,这种请求被打包成命令方式。Handler以类似MVC控制器的方式来接受命令或查询(也可以是来自异步消息机制),然后递交给领域模型或查询模型进一步处理。查询模型与领域模型是两种完全不同的模型,领域模型是完全基于DDD原则建立的,而查询模型则是根据数据查询要求来建立的,两种模型的设计依据不同当然基于领域模型实现各种查询也是有好处的。领域模型中提供了业务逻辑规则,这种规则可能对输出查询也是有作用的

关于Command对象

CQRS是命令和查询分离的模式或架构,因此Command命令对象是CQRS的关注重点。命令表达用户的意图,是用户希望计算机系统做想让它做的事情,命令通过发出请求的方式到达计算机系统,通过响应让用户知晓他的命令是否被计算机成功执行

传入领域层的数据都是以命令对象的形式封装的,这样领域层所依赖的输入数据不再是DTO,因为DTO属于技术级别,如果领域模型的输入参数依赖DTO,那么就会造成领域层依赖技术层,这就违背了领域驱动设计的宗旨。

作为CQRS架构实现,领域模型接受的应该是命令,也就是用户的意图表达

CQRS的好处之一是将领域驱动设计和数据驱动设计分离,在查询读取模型中可以使用数据驱动设计,而在命令传入执行的模型路线中必须使用领域驱动设计,因此,从请求的起点开始,沿着请求传入方向,逐个检查请求经过的各个环节,以确保请求数据在传入领域层之前已经被转换为命令对象

当然,这样系统还有很多的类,他们服务于不同的分层,有着不一样的单一的职责:Form服务于表现层、DTO负责在技术级别层次之间传输纯数据、命令服务于领域层、领域模型的实体服务于业务领域和需求、仓储实体服务于数据表、数据表服务于长久保存的目的。

如果用户界面希望显示什么字段,可直接在Form对象中增加,是不是需要放入命令对象,那就要考察这个字段是否属于业务领域范畴,如果这个字段是控制显示方式的,那么它属于应用程序的逻辑,不是业务逻辑,当然不需要放入命令对象;如果属于业务逻辑放入命令对象了,也要考察这个字段对领域模型的影响:为什么设计领域模型时没有考虑这个字段,是否意味着流程改变或分支流程的出现,是否会出现新的有界上下文和聚合(这个影响比较大)。

应用逻辑和业务逻辑的区别需要一定的敏感性,学习领域驱动设计的一个好处就是培养业务逻辑的识别,有了业务逻辑识别,就自然对应用逻辑变得敏感了,这样就能逐渐走上业务与技术分离的架构路线,保证业务逻辑在领域模型中得到不断重构和发展,成为系统的核心资产。

1.6.2. 不同的数据访问方式

  1. 应用CQRS的第一步是将大量服务重构为单独的查询和命令

  2. 实现CQRS的第二步是为查询模型和命令模型使用不同的存储引擎。例如,ElasticSearch用于查询端,JPA/MySQL/Oracle用于命令端,使用MongoDB等NoSQL或Redis缓存用于查询,在命令端的RDBMS中将聚合存储为JSON。当然,不同的存储引擎之间也需要互相同步

    Untitled

1.6.3. 领域事件实现数据同步

基于领域事件实现数据同步也是基于不同的存储数据库,将聚合中发生的事件通过消息发送给查询端,在查询端订阅该领域事件,一旦更改事件送达,就执行这个事件,将其转为查询端的视图结构,这个过程称为投影(Projection)。投影是将事件流转换为结构表示的过程,结构表示有许多其他名称:持久性读取模型、查询模型或视图:

Untitled

这种方式的特点在于命令模型:这里使用EventStore作为持久存储,而不是RDBMS和ORM;不保存实际的对象状态,而是保存事件流。这种模式被命名为事件溯源(EventSourcing),后文讨论。

2. 各种架构总结

从清洁架构、六边形架构到CQRS架构,这些架构都从不同方面关注系统的职责功能。清洁架构和六边形架构是将业务与技术相分离,这是一个大的分离解耦方向,而CQRS则是将业务领域分离为查询和命令两种方式。通过这两种不同方式的切分,DDD领域模型将切实得到隔离和保护,这些领域模型能够不受各自具体技术发展的影响,成为组织内真正的核心资产

3. 参考资料

标签:架构,软件设计,模型,业务,领域,命令,CQRS,查询
来源: https://blog.csdn.net/qq_20021569/article/details/123598023