前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >我将项目使用DDD经典四层架构重构后,如何采用CQRS解决查询问题

我将项目使用DDD经典四层架构重构后,如何采用CQRS解决查询问题

作者头像
吴就业
发布2020-11-04 16:13:28
2.7K0
发布2020-11-04 16:13:28
举报
文章被收录于专栏:Java艺术Java艺术

笔者最近一直在学习DDD,包括看完几本领域驱动设计TOP的书籍,以及极客时间的《DDD实战课》专栏,并且也看了很多demo案例。有趣的是,由于DDD并不像MVC架构那样有统一的模版,每个作者实现的架构风格截然不同,这也给我提供了很多参考价值,感谢这些大佬!

若要我推荐的话,我更乐意向大家推荐《领域驱动设计(Thoughtworks洞见)》这本书,这本书案例很多,对于初学者来说更容易理解DDD,这本书给我的启发很大。(我并不推荐大家使用四层架构,而更喜欢《领域驱动设计(Thoughtworks洞见)》这本书中DEMO的架构风格)

作为初学者,我尝试将新项目架构改为DDD四层架构后,遇到很多问题,性能问题是一方面,另一方面我也一直在思考如何优雅的实现查询,诸如报表统计类的查询(含分页查询)。也因为是应用在实际项目中,所以才一直逼着自己去解决这些问题,项目架构也每天在调整。

我看了很多书、网上找资料,其实就是想看看别人都是如何实现查询功能的,特别是分页报表类的查询、以及在查询性能方面的考虑。

其实是自己一开始就将思维限制在了死胡同,将思考方向固定在通过聚合根/领域服务实现,方向一错就很难找到答案,为此我付出了很多时间不断的调整代码、不断的调整代码。

最后在《领域驱动设计(Thoughtworks洞见)》这本书中找到了我想要的答案。

根据书中给出的三种实现方式,笔者综合我们项目当前使用的DDD经典四层架构现状,在四层架构的基础上做了些许改动。

报表统计分页查询优化

优化报表统计分页查询流程如下:

接口层接收请求 -> 应用层处理请求 -> 直接调用DAO查询

这不就是MVC三层架构吗?是,也不是!

报表统计一般会查询OLAP数据库,即便没有将数据同步到一个分析型的数仓,也应该会将数据同步到一个从库,或者由定时任务按天/按月统计数据,将统计数据生成一个新的表。在数据同步的过程,为了降低数据存储的成本,也为提升查询的性能,经常需要将原表数据统计/删减后再存入一个新表/从库/分析型数据库,并且数据不会再修改。

可见,数据查询分析并不需要处理业务逻辑,在DDD建模时,也不会考虑数据分析的情况,所以数据分析应该绕过领域建模,绕过聚合根、Repository,直接从数据库读取数据。最好的方式当然是将报表统计数据分析放到一个独立的项目去做。

非报表统计查询采用CQRS优化

CQRS(Command Query Resposibility Segregation),即命令查询职责分离,软件模型中存在读模型和写模型之分,以我们写业务代码的经验也知道,一次请求,要么是作为一个“命令”执行一次操作,要么作为一个”查询“向调用方返回数据,两者不可能共存。CQRS是将“命令”和“查询”分别使用不同的对象模型来表示。

共享存储-共享模型-CQRS

共享存储指同一个表结构存储数据,共享模型指使用聚合根从数据库读取数据。

例如查询订单详情,订单的聚合根为Order

代码语言:javascript
复制
// 订单聚合根
public class Order extends BaseAggregate {
    public OrderRepresentation toRepresentation() {
    }
}

在应用层OrderApplicationService通过OrderRepository查询订单聚合根,再调用toRepresentation将聚合根转为读模型。

代码语言:javascript
复制
public class OrderApplicationService {
    public OrderRepresentation byId(String id) {
        Order order = orderRepository.byId(id);
        return order.toRepresentation();
    }
}

Order聚合根的toRepresentation方法是将Order转为读模型实体,屏蔽一些信息。在经典四层架构中,由应用层专门的一个类负责将聚合根转为读模型实体(这里是DTO),也就是将DO转为DTO,在采用CQRS后,Order聚合根为写模型,OrderRepresentation为读模型,对应DTO

笔者采用的是极客时间《DDD实战课》推荐的经典四层架构,应用读模型还是使用DTOtoRepresentationassembler(装配工)完成。

为了更好的区分读和写,OrderApplicationService应该只负责写请求,而创建新的OrderRepresentationService负责读请求(并非所有读请求),接口层的OrderController同时依赖OrderApplicationServiceOrderRepresentationService

共享存储-读写分离模型-CQRS

共享存储-读写分离模型指读写还是操作同一张表,只是写模型与读模型不同,写通过聚合根操作,而读模型绕过聚合根、Repository,直接操作数据库,此时的读模型就是用于装载从数据库查询的数据,并且不需要再作转换就可以响应给调用方,这里的读模型相当于DTO

对于单个聚合根内的查询,使用此模型可以应付复杂的查询场景,并且可以提升性能。例如,查询订单信息时,我们可能并不需要获取订单下的每个子订单OrderItem

对于需要跨多个聚合根的查询,共享模型无法实现此需求场景,而分别查询多个聚合根后,再合并查询结果不仅是将原本简单的事情变复杂,还大大影响性能,因此更有必要采用读写分离模型。

例如查询订单详情希望带上商品信息。

如果商品与订单在同一个服务,并且同一个数据库,那么便可以使用join多表查询。

如果商品是一个微服务、订单是一个微服务,并且两个微服务使用不同的数据库,那么就只能是分别查询多个聚合根后,再合并查询结果。对于此场景,如果要提升性能,就需要通过额外的数据同步服务,将订单与商品查询结果合并后存入一个新的表,或者是存储到NoSQL数据库。

CQRS的读操作放在四层架构(应用层、领域层、基础架构层、接口层)中的应用层,而不是领域层,因为读操作不仅仅只是查询数据库,更多时候也会查询缓存、ES。领域层负责写、缓存数据同步通过消费领域事件方式同步。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-10-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java艺术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 报表统计分页查询优化
  • 非报表统计查询采用CQRS优化
    • 共享存储-共享模型-CQRS
      • 共享存储-读写分离模型-CQRS
      相关产品与服务
      对象存储
      对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档