Day 27 「能省则省」Clean Architecture ft. CQRS

大家有看过以前乡下那种烧柴的大灶吗?


图片截自爆废公社

记得以前小时候,阿公阿嬷家有这种烧柴的大灶,阿公下田干活时,阿嬷就去到处捡柴回来堆在院子里,晚上要洗澡时就烧热水来用。那时家里虽然已经有热水器,但阿嬷还是每天坚持用大灶烧水,问她为什麽不用热水器就好,「安呢卡省啦!」她总是简单这麽回答着,直到後来年纪渐渐大了,开始抬不太动大锅水了,她才慢慢不再坚持,开始用热水器洗澡。

在用一个「申请奖学金」的范例看完一次完整的指令以後,你应该也会觉得:「蛤!好麻烦喔!」

是的,如果每个指令都这样设计,的确是会做不少额外的工作。复杂运算就算了,但有时候,前端就只是想跟後端要一些资料来显示,也要分这麽多层,这令我们不禁担心,这种 overhead 真的值得吗?这种情况下,是否有比较「省事」、比较「轻量级」的做法?

有喔!

今天我们就来聊聊「命令查询职责分离模式」(CQRS - Command Query Responsibility Segregation)。

命令查询职责分离模式

Ajay Kumar 在 2019 年出的 CQRS 一书中,建议我们把所有对系统的指令,分成不会改变系统状态的「查询(Query)」与会改变系统状态的「命令(Command)」。以我们的教务处网站系统为例,「奖学金列表」就是一种查询,「申请奖学金」就是一种命令。

Ajay Kumar 告诉我们,查询指令不会改变系统状态,而且经常会改动,所以不需要劳烦 Entity 与 Service 出动,只要叫 Controller 直接呼叫 特定的 Repository 去查就好。除了比较简单以外,因为 Entity 没有参与流程,所有前端需求的变动,也不会动到系统核心的 Entity 设计。

CQRS 的副作用:假性重复

Uncle Bob 曾提醒我们,要分辨真正的重复与假性重复。什麽意思?不是说重复是万恶之渊,必除之而後快吗?事到如今怎麽突然冒出了个什麽假性的重复,是这搞啥鬼?

这时,就得回头看看我们讲的「重复」到底坏在哪里了。重复之所以讨厌,在於一旦两个相异物件长得一模一样,当需求有改,两个得一起改。那如果这两个物件虽然长一样,但不只代表不同东西,而且也不会一起改呢?

胡说!哪有这种东西?

有的,在 Clean Architecture 的系统中,同时套用 CQRS 就会。

在 Query 时,因为不用走进 Service,这时 Controller 会跟 Repository 要求一个特制的物件回传格式,在不失语意的假设下,我们先称之为 VO。VO 与 Entity 在系统开发初期可能长得很像,譬如捞取奖学金列表时,Controller 很可能需要捞出跟 Entity 一模一样的东西。

这时,根据 Ajay Kumar 的建议,要忍住,不可以直接拿 Entity 去回传,因为我们一直强调的,前端画面变动得很快,如果此时 Controller 回的是 Entity,不但违反了 Clean Architecture 的跨层原则,更糟糕的是,它让系统的核心 Entity 随着最外层的 UI 快速变化,但,你知道的, UI 的变化根本不是核心该管的事情,所以,这时要回一个「为这个 Query 客制化的 VO」。

於是重复就出现了。这使我们陷入进退维谷的窘境,这该如何是好?好佳在,Uncle Bob 这时跳出来解救我们了。他告诉我们,VO 与 Entity 长得像,或是不同 Query 的客制化 VO 长得像,是一种「假性重复」,这种重复不只不会致命,你放着不处理,随着 UI 慢慢改变,这些 VO 自己会各自演化,最终很有可能也会长得不一样,如果当初硬着头皮去消重复,反而会阻碍这种自然演化,造成维护上的困难。

於是我们就放心了,既然无害,那就随它去吧!

谜之声:「车过桥头往冈山,船到桥头至楠梓。」


图片截自 YouTube

Reference

  1. Ajay Kumar, CQRS (Command Query Responsibility Segregation), Independently Published, 2019
  2. Z-xuan Hong,看到重复就删除? 小心不要看到黑影就开枪:https://tinyurl.com/5fvwf6ya
tags: ithelp2021

<<:  postman

>>:  [ Day 12 ] React 的生命周期 - Updating

D16 第八周 (回忆篇)

这礼拜进度开始落後,在写第七周作业的 todo list,自己後来才发现事件代理的用法,所以後面又重...

今年我想陪着 30 天之 30

832. Flipping an Image Given a binary matrix A, we...

Day 9 - 基本语法4 (布林值)

布林(英语:Boolean) 在computer science中作为判断使用,以发明布林代数的数学...

【LeetCode】当初的 LeetCode 学习

有点受不了没什麽演算法底子还要掰出 Leetcode 文章QQ 今天来记录当初学习的过程和後来的想法...

安能取熊掌而舍鱼? 便捷初始化器语法

安能取熊掌而舍鱼? 便捷初始化器语法 今天听到坐在我旁边的 Ray 提到便捷初始化语法,我惊了,事实...