第 6 / 10 部 待錄

架構核心

《Clean Architecture》Podcast 準備稿:架構核心

書名: 整潔的軟體架構 Clean Architecture(A Craftsman’s Guide to Software Structure and Design) 作者: Robert C. Martin(Uncle Bob) 系列: Reading Uncle Bob(恩普拉氏) 公開標題建議: Reading Uncle Bob|Clean Architecture.第 16-21 章:讓你的程式碼「尖叫出」它在做什麼 (6/10) 涵蓋範圍: 第 16 章「獨立性」、第 19 章「策略和層級」、第 20 章「業務規則」、第 21 章「會尖叫的架構」

背景速覽

這一集是整本書的心臟。前面幾集我們鋪了一堆原則——SOLID、元件內聚、依賴反轉——但那些都還停在「規則」的層次。從這集開始,Uncle Bob 要把這些規則組裝成一個有血有肉的架構觀。

四章可以串成一條很清楚的線:先問「一個好架構要支援什麼」(獨立性,第 16 章),再問「系統裡到底什麼是高層、什麼是低層」(策略和層級,第 19 章),接著定位「業務規則住在哪裡」(實體與使用案例,第 20 章),最後檢驗「打開專案資料夾,它在對你尖叫什麼」(會尖叫的架構,第 21 章)。

如果你只聽全系列一集,建議就是這集。因為「依賴方向要指向核心業務」這個念頭,是後面所有招式的母題。但我也想誠實說:這集講的是一個「理想國」,而真實世界的框架預設值,常常跟這個理想國背道而馳。我們最後會好好辯這件事。

一句話重點

好架構讓「系統在做什麼業務」成為一等公民,並強制讓所有易變的細節(UI、資料庫、框架)去依賴穩定的核心業務規則,而不是反過來。

值得討論的重點

第一,獨立性的四個面向,其實是四種「未來的自由」。Uncle Bob 說好架構要同時支援使用案例、運行、開發、部署。翻成白話:使用案例顯眼,讓人一眼看懂系統意圖;運行可分離,效能不夠時能把某個服務搬到別台機器;開發可隔離,多團隊並行不互相踩腳(這裡他搬出康威定律——架構會長成組織的形狀);部署可熱插拔,加功能只要丟一個新的 jar 進去,不必重編譯整包。注意這四點的共同武器只有一個字:解耦。

第二,他給「層級」下了一個很反直覺、但極度精準的定義:層級 = 距離輸入與輸出的遠近。不是「誰比較重要」,不是「誰寫得比較早」,而是離 I/O 多遠。讀 SQL、渲染 HTML 的程式碼是低層,因為它直接貼著 I/O;核心業務邏輯是高層,因為它根本不在乎資料從哪來、要顯示到哪去。有了這個尺,依賴規則就一句話:原始碼依賴永遠指向高層。為什麼?因為低層(I/O 相關)變得又頻繁又瑣碎——換 UI、升級驅動、改報表格式;高層(業務核心)變得又少又重大。我們當然要讓「常變的」去依賴「穩定的」,而不是讓核心被 I/O 的風吹草動牽連。

第三,業務規則的兩層切分,是這整套架構最值得記住的區分。Entities(實體)是關鍵業務規則——銀行算利息的公式,就算沒有電腦、行員拿紙筆也照樣成立。它是系統裡最高層、不依賴任何東西的淨土。Use Cases(使用案例)是應用程式專屬規則——它定義「使用者怎麼操作這套自動化系統」,協調實體運作。關鍵在依賴方向:使用案例依賴實體,但實體不知道使用案例的存在。而且他下了一條鐵律——別讓使用案例去繼承 HttpRequest、別讓它認得 SQL;輸入輸出就用單純的 Request/Response 資料結構。這樣同一套業務規則才能同時服務 Web、Console、或任何還沒出生的介面。

第四,會尖叫的架構,是把前面三點濃縮成一個畫面感極強的比喻。打開圖書館的藍圖,你看到書架區、閱讀桌、櫃檯,它在尖叫「我是圖書館」。打開你的專案最上層資料夾,它應該尖叫「我是庫存管理系統」或「我是會計系統」,而不是尖叫 controllers/views/models/——那只是在喊「我是 Rails」「我是 Spring」。Web 只是一個交付機制、一個 I/O 裝置,不是核心。一個好架構的副產品就是可測試性:使用案例是普通物件,你不必啟動伺服器、連資料庫,就能在原地測它。

注意事項

⚠️ 「會尖叫的架構」很對,但你得承認:大多數主流框架的預設結構,本來就是「按框架尖叫」的。Rails 給你 app/controllersapp/models;Django、Spring、Laravel 也都各有一套「框架味」資料夾。Uncle Bob 要你逆著這個慣例、改用「按業務領域分層」(例如頂層放 billing/inventory/shipping/)。這在錄音時要誠實點出:逆框架而行是有成本的——你放棄了框架的「慣例優於配置」紅利,新人反而要重新學你的自訂結構,框架升級時的腳手架、generator、社群文件也都對不上。所以這不是「對 vs 錯」,是「業務表達力 vs 框架慣例紅利」的取捨。

⚠️ 業務規則與框架解耦的「純粹性」,跟開發速度直接打架。Uncle Bob 的理想是 Entities 不繼承任何框架基底類別、Use Case 不認得 ORM。但實務上,你若堅持讓 Entity 完全脫離 ORM,往往得手寫一整層 mapper、維護兩套幾乎一樣的資料結構(這正好呼應第 16 章他說的「假重複要容忍」)。對一個 CRUD 為主、業務邏輯很薄的系統,這層純粹性可能買不到對應的價值,反而拖慢交付。辯證的點是:純粹性的收益跟「核心業務邏輯的厚度」成正比——邏輯越厚、越值錢、越會變,解耦越划算;邏輯越薄,越接近「直接讓 Entity 當 ORM model」反而更務實。

⚠️ 「層級 = 距離 I/O 的遠近」是個漂亮的定義,但別把它當成可以機械套用的公式。真實系統裡,常有一塊邏輯「既貼著 I/O 又承載重要業務規則」(例如金流對帳,既要讀第三方 API,又是核心賺錢邏輯)。這時硬要把它切成「高層 policy + 低層 adapter」會很彆扭。要提醒聽眾:分層是判斷,不是查表。

⚠️ DRY 在架構層次會反咬你。Uncle Bob 特別警告:把資料庫 Entity 直接拿來當 UI 的 View Model,因為「欄位長得一樣」——這是經典陷阱。UI 跟資料庫會為了完全不同的理由而變,綁在一起,未來改個畫面就意外動到資料層。所以要區分「真重複」(永遠為同一理由而改,該消除)和「假重複/意外重複」(現在像、未來會分岔,該容忍)。這點很反直覺,值得在節目裡慢慢講。

專家補充

💡 這套「Entities / Use Cases / 依賴指向核心」其實不是 Uncle Bob 原創,而是同一個老想法的第 N 次轉世。Ivar Jacobson 1992 年的《Object-Oriented Software Engineering》就在講 use-case driven;Alistair Cockburn 的 Hexagonal(Ports & Adapters)、Jeffrey Palermo 的 Onion Architecture,講的都是同一件事——核心在內、I/O 在外、依賴向內。錄音時點出這個「家族族譜」,能幫聽眾把名詞對齊,不會以為這是 2017 年才冒出來的新發明。

💡 想立刻動手驗證自己有沒有做到「會尖叫」,有個一分鐘自測:把專案核心模組的依賴關係抓出來(Java 可用 ArchUnit,前端可用 dependency-cruiser),看核心業務套件有沒有 import 到 framework 或 ORM 套件。如果有,箭頭就反了。這比口頭爭論「我們架構乾不乾淨」有用得多——讓工具去尖叫。

💡 一個常被忽略的細節:第 20 章的 Request/Response Model 跟後來流行的 CQRS、以及 DDD 的 Application Service 幾乎是同一個位置的東西。如果聽眾團隊已經在用 DDD,可以直接告訴他們:Uncle Bob 的 Use Case ≈ DDD 的 Application Service,Entity 這個詞兩邊也都有但內涵略有差異(DDD 的 Entity 強調身分識別,Uncle Bob 的 Entity 強調關鍵業務規則)。先講清楚,免得術語打架。

討論問題

🎙️ 開場可以丟給聽眾一個自我檢查:現在打開你手上專案的最上層資料夾,它在尖叫什麼?是尖叫業務(billing、ordering),還是尖叫框架(controllers、services、repositories)?讓聽眾在通勤路上腦中跑一遍自己的 repo。

🎙️ 第二個我想單獨展開的問題:你真的需要讓 Entity 脫離 ORM 嗎?我會分兩種系統來回答——一種是業務邏輯薄、CRUD 為主的後台,一種是業務邏輯厚、規則常變的核心系統,然後現場推導兩者的最佳解為什麼不同。

🎙️ 第三個拿來收尾的反思:康威定律說架構會長成組織的形狀。那麼,當我們抱怨「我們的架構很亂」時,有多少其實是在抱怨「我們的組織很亂」?架構問題能不能純靠技術手段解決?這題我會留開放,請聽眾在留言區接力。

更大範圍關聯

向前接 EP1-5:這集是把前面所有原則(SOLID 的 DIP、元件的穩定依賴原則)「組裝成形」的一集。如果前面是零件,這集是第一次看到整台引擎。

向後接 EP7「邊界專題」:這集講「為什麼要把核心跟細節分開、依賴該往哪指」,下一集就直接問「那條分隔線到底畫在哪、什麼時候畫、要不要畫滿」。EP6 是信念,EP7 是成本核算——兩集是一體兩面,最好連著聽。

跨界連到 DDD 與六角架構:Hexagonal、Onion、Clean 是同一思想的三個品牌。聽完這集,聽眾應該能在面試或設計評審裡,把這三個名詞講成「同一件事的不同包裝」。

連到現代前端與微服務:「Web 只是交付機制」這句話,在今天 SSR/CSR/RSC 混戰、以及微服務拆分的年代,反而更值得拿出來校準——很多團隊把交付機制當成了架構主軸。

錄製建議

目標約 20 分鐘,單人主講。建議節奏:開場 2 分鐘用「圖書館藍圖」的比喻直接抓耳朵,把這集要回答的問題立起來——「打開你的資料夾,它在尖叫什麼?」中段 12 分鐘走四章主線,但不要平均分配時間:把火力集中在第 19 章的「層級 = 距離 I/O」這個定義,和第 20 章的 Entities vs Use Cases,這兩塊是聽眾最容易帶走的乾貨;第 16、21 章用比喻快速帶過即可。最後 6 分鐘進「注意事項」的辯證——這是本台招牌,務必留足時間,把「逆框架的成本」和「純粹性 vs 開發速度」講得有來有回,不要只站 Uncle Bob 那一邊。收尾用討論問題的第三題(康威定律)留一個餘味。

策略提醒:這集概念密度高,單人講容易變成念稿。建議每講完一章就插一個「你自己的 repo 是怎樣?」的反問,把抽象拉回聽眾的工作現場。涉及依賴方向時,用「箭頭往哪指」這種口語視覺化來講,比念「DIP」三個字母好懂得多。