大家有看过以前乡下那种烧柴的大灶吗?
图片截自爆废公社
记得以前小时候,阿公阿嬷家有这种烧柴的大灶,阿公下田干活时,阿嬷就去到处捡柴回来堆在院子里,晚上要洗澡时就烧热水来用。那时家里虽然已经有热水器,但阿嬷还是每天坚持用大灶烧水,问她为什麽不用热水器就好,「安呢卡省啦!」她总是简单这麽回答着,直到後来年纪渐渐大了,开始抬不太动大锅水了,她才慢慢不再坚持,开始用热水器洗澡。
在用一个「申请奖学金」的范例看完一次完整的指令以後,你应该也会觉得:「蛤!好麻烦喔!」
是的,如果每个指令都这样设计,的确是会做不少额外的工作。复杂运算就算了,但有时候,前端就只是想跟後端要一些资料来显示,也要分这麽多层,这令我们不禁担心,这种 overhead 真的值得吗?这种情况下,是否有比较「省事」、比较「轻量级」的做法?
有喔!
今天我们就来聊聊「命令查询职责分离模式」(CQRS - Command Query Responsibility Segregation)。
Ajay Kumar 在 2019 年出的 CQRS 一书中,建议我们把所有对系统的指令,分成不会改变系统状态的「查询(Query)」与会改变系统状态的「命令(Command)」。以我们的教务处网站系统为例,「奖学金列表」就是一种查询,「申请奖学金」就是一种命令。
Ajay Kumar 告诉我们,查询指令不会改变系统状态,而且经常会改动,所以不需要劳烦 Entity 与 Service 出动,只要叫 Controller 直接呼叫 特定的 Repository 去查就好。除了比较简单以外,因为 Entity 没有参与流程,所有前端需求的变动,也不会动到系统核心的 Entity 设计。
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
ithelp2021
>>: [ Day 12 ] React 的生命周期 - Updating
这礼拜进度开始落後,在写第七周作业的 todo list,自己後来才发现事件代理的用法,所以後面又重...
832. Flipping an Image Given a binary matrix A, we...
布林(英语:Boolean) 在computer science中作为判断使用,以发明布林代数的数学...
有点受不了没什麽演算法底子还要掰出 Leetcode 文章QQ 今天来记录当初学习的过程和後来的想法...
安能取熊掌而舍鱼? 便捷初始化器语法 今天听到坐在我旁边的 Ray 提到便捷初始化语法,我惊了,事实...