文章目录
前言
DDD 作为一种系统分析的方法论,最大的问题是如何在项目中实践。而在实践过程中必然会面临许多的问题,「模式」是系统架构领域中一种常见的手段,能够帮助开发人员与架构师在遭遇某种较为棘手,或是陌生的问题时,参考已有的成熟经验与解决方案,从而优雅的解决自己项目中的问题。
本篇介绍的是CQRS,中文名为命令查询职责分离。
一、 CQRS介绍
1.什么是 CQRS
CQRS — Command Query Responsibility Segregation,故名思义是将 command 与 query 分离的一种模式。query 很好理解,就是我们之前提到的「查询」,那么 command 即命令又是什么呢?
CQRS 将系统中的操作分为两类,即「命令」(Command) 与「查询」(Query)。命令则是对会引起数据发生变化操作的总称,即我们常说的新增,更新,删除这些操作,都是命令。而查询则和字面意思一样,即不会对数据产生变化的操作,只是按照某些条件查找数据。
CQRS 的核心思想是将这两类不同的操作进行分离,然后在两个独立的「服务」中实现。这里的「服务」一般是指两个独立部署的应用。在某些特殊情况下,也可以部署在同一个应用内的不同接口上。
Command 与 Query 对应的数据源也应该是互相独立的,即更新操作在一个数据源,而查询操作在另一个数据源上
2.为何要使用CQRS
毋庸置疑「领域」在 DDD 中占据了核心的地位,DDD 通过领域对象之间的交互实现业务逻辑与流程,并通过分层的方式将业务逻辑剥离出来,单独进行维护,从而控制业务本身的复杂度。
但是作为一个业务系统,「查询」的相关功能也是不可或缺的。在实现各式各样的查询功能时,往往会发现很难用领域模型来实现。假设在用户需要一个订单相关信息的查询功能,展现的是查询结果的列表。列表中的数据来自于「订单」,「商品」,「品类」,「送货地址」等多个领域对象中的某几个字段。这样的场景如果还是通过领域对象来封装就显的很麻烦,其次与领域知识也没有太紧密的关系。
此时 CQRS 作为一种模式可以很好的解决以上的问题。
二、CQRS 架构
CQRS 建议将应用程序层分为两个方面,即命令端(Command)和查询端(Query)。
查询端负责优化读取数据。从持久化获取数据,然后将它们映射到展现层表单,这些表单通常被标识为数据传输对象(DTO)。
命令端关注优化写入数据。命令执行各种用例,修改实体状态并将其持久化。
通过分离读写操作,我们提高了性能,并在系统中支持关注点分离原则。
本文介绍 3 种主要的 CQRS 架构实现。
1.单数据库 CQRS
顾名思义,双方都在和一个数据库对话。Command 在域中执行用例,从而修改实体的状态,然后通过 ORM 如 Entity Framework Core 或 Hibernate 将实体保存到数据库中。
Query 直接通过数据访问层执行,数据访问层要么是使用各种 ORM,要么通过存储过程。
2.双数据库 CQRS
在“双数据库”方式中,我们需要两个数据库,一个用于写操作,一个用于读操作。命令端使用针对写操作优化的数据库。查询端使用针对读取操作优化的数据库。
命令每改变一个状态,修改后的数据就必须从写数据库推送到读数据库中,或者作为一个跨两个数据库的分布式事务,或者使用最终一致性模型。
这种架构给软件的查询端带来了数量级的性能提升,这是有利的,因为一般系统在读数据上花费的时间一般比写数据要更多。
3.事件源 (Event source) CQRS
最后一种是最复杂的 CQRS 架构。与前面两种方式相比,事件源存储数据的思路完全不同。
在事件源方法中,我们并不只存储实体的当前状态,而且将实体发生的每一个状态作为快照来存储。实体并不是以标准化数据的形式保存,而是通过事件的时间戳来保存它们的变更。
从图上可以看到,当 command 系统完成数据更新的操作后,会通过「领域事件」的方式通知 query 系统。query 系统在接受到事件之后更新自己的数据源。所有的查询操作都通过 query 系统暴露的接口完成。
事件源带有以下好处:
- 事件存储包括完整的审计跟踪,可以在需要严格监管的场景中派上用场。
- 可以在任何时间点重建任何实体的任何状态,这对于调试非常有用。
- 可以重放事件,查看系统中任何时候到底发生了什么。这个功能对于压力测试和 bug 修复非常有用。
- 可以轻松地重建生产数据库。
- 有多个为读优化的数据存储。
但在另一方面,这种方式实现很复杂,如果你不能从其中受益,那么用这个模式可能适得其反。
结尾
- 感谢大家的耐心阅读,如有建议请私信或评论留言。
- 如有收获,劳烦支持,关注、点赞、评论、收藏均可,博主会经常更新,与大家共同进步