DDD 与 CQRS 才是黄金组合

在日常工作中 , 你是否也遇到过下面几种情况:

  • 使用一个已有接口进行业务开发,上线后出现严重的性能问题,被老板当众质疑:“你为什么不使用缓存接口,这个接口全部走数据库,这怎么能扛?。?rdquo;
  • 开发一个后台管理功能,业务反馈说数据一直不对 , 对比后发现缓存与数据库不一致,为什么要使用缓存接口呢,你陷入沉思?
  • 产品要求在 xxx 上增加新功能,编码、测试、上线一气呵成 , 最后发现另外一个流程被躺枪,出现异常不得不进行回滚!
  • 在一个高并发的场景,DB 成为了系统瓶颈,不加索引查询扛不?。?铀饕??驴覆蛔?nbsp;, 又该如何处理?
  • 随着数据量的激增 , 系统变得越来越慢 , 特别是后台管理复杂的查询场景下,复杂的 Join 让 DB 不堪重负 。
  • ……
为什么会出现这种现象?其本质仍旧是代码组织结构不合理,我们将不同的复杂性揉在一起,从而造成了更大的复杂性,然后如此往复,不知不觉中陷入巨大的复杂性漩涡不可自拔 。
一、CQRS 是什么?
CQRS 是 Command Query Responsibility Segregation 的简称,简单理解就是对 “写”(Command) 和 “读” (Query)操作进行分离 。反应快的同学会说:“也不是什么高深技术吗,不就是数据库的读写分离吗?”
是的,数据库的读写分离也算是一种 CQRS,但 CQRS 的含义要比这复杂得多 。
CRQS 既是一种流行的业务架构,又是一种设计思维 。
CQRS 的核心是“拆分”,将复杂系统拆分为 Command 和 Query 两个部分 , 针对不同的场景使用不同的模式,选择最合适的技术落地最佳解决方案,避免两者相互掣肘相互影响 。
CQRS的目的是降低整个系统的复杂性,那它背后的逻辑是什么?
假设,在一个系统中:
  • Command 的复杂性为 M
  • Query 的复杂性为 N
如果使用同一套模型来处理 Command 和 Query,那在极端情况下,系统的复杂性为 M * N,因为两者相互影响,调整一方的同时要时刻关注对另一方的影响 。
DDD 与 CQRS 才是黄金组合

文章插图
这种“你中有我,我中有你”的设计方式,“两者的相互影响”成为系统最为复杂之处,大量精力消耗在“排查影响”,而非最有价值的设计和编码 。
如果,将 Command 和 Query 彻底分离,系统的复杂性变成 M + N 。Command 的变更不会影响 Query,而 Query 的修改也不会影响 Command 。
DDD 与 CQRS 才是黄金组合

文章插图
当然,以上两个极端在实际工作中也很少见,通常系统的复杂性介于两者之间 。
DDD 与 CQRS 才是黄金组合

文章插图
这只是从理论进行推导,在实际工作中随处可见的“冲突”也是对“拆分”的一种暗示 。
二、分层架构中的冲突
以最常见的分层架构进行介绍,具体如下:
DDD 与 CQRS 才是黄金组合

文章插图
如图所示,将系统分成5层,每层的含义如下:
  • Web 接入层 。主要用于处理系统输入,对输入信息进行验证,调用应用服务完成业务操作,对结果进行转换,最终返回给调用方;
  • 应用服务层 。主要处理业务流程编排,从仓库中获取领域对象,执行领域模型的业务操作,将最新的对象状态通过仓库同步到数据存储引擎,并对外发布领域事件;
  • 领域层 。业务逻辑的承载点,是业务价值的集中体现,通常构建于面向对象设计之上,基于封装、继承、多态等特性保障业务逻辑的复用性和扩展性;
  • 仓库层 。主要用于数据访问,向上为应用服务提供数据操作服务,向下屏蔽各类存储引擎的差异;
  • 数据层 。主要用于数据保存和检索,常见的数据存储引擎全部属于这一层,比如 MySQL、redis、ES 等;
其实,分层架构本身也是一种“拆分”,将不同的关注点封装在不同的层次 。但除了横向分层,还可以基于 CQRS 对其进行纵向拆分,也就是将每个层的组件拆分为 Command 和 Query 两部分 。
由于接入层冲突较?。?旧聿鸱值囊庖宀淮?,在此不做要求,但从严格意义上讲,仍旧建议进行拆分 。
三、应用服务层冲突与拆分
应用服务层拆分就是将一个应用服务拆分为 CommandService 和 QueryService 两组 。


推荐阅读