第 9 / 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.第 27-31 章:資料庫是細節、Web 是細節,這話太狂了嗎 (9/10) 涵蓋範圍: 第 27 章「服務:偉大與微小」、第 29 章「整潔的嵌入式架構」、第 30 章「資料庫是細節」、第 31 章「Web 是細節」

背景速覽

上一集我們把同心圓本尊講透了——核心就一句話:依賴只能向內。這一集,Uncle Bob 開始拿這把尺去量四個「大家平常超級在意」的東西:微服務、嵌入式韌體、資料庫、Web。

然後他講出了全書最狂、最容易被截圖出來吵架的兩句話:「資料庫是細節」「Web 是細節」

我想先說:這一集裡,Uncle Bob 有一個論點我覺得超級有先見之明、值得大大肯定——「微服務不會自動幫你解耦」;但也有兩個論點,是這本書最該被現代實務挑戰的地方。這集會很好玩,因為它同時展示了 Uncle Bob 最銳利和最理想化的一面。我們吸收,也辯證。

一句話重點

Uncle Bob 把微服務、韌體、資料庫、Web 全都降級成「細節」——架構邊界是邏輯的(依賴反轉與穩定介面),不是物理的(跨不跨行程、用不用 SQL、走不走 HTTP);切成多少個服務、用哪套 DB,都只是部署選項。

值得討論的重點

重點一:微服務的「解耦幻覺」——這點 Uncle Bob 神準

人人都以為:服務跑在不同行程、走網路通訊,所以彼此「嚴格解耦」。Uncle Bob 潑冷水:物理上的分離,遮蓋不了邏輯上的耦合。只要兩個服務共享資料——共用一段 JSON 欄位、更糟的是共用同一個資料庫 schema——它們就完全沒解耦。你改一個欄位,兩邊一起爆。這就是大家後來叫的「分散式單體」。他還補一刀:服務邊界不等於架構邊界,新功能(書裡的例子是 Uber 加「載運寵物」)若橫跨 UI、計費、媒合三個服務,這三個團隊就得協調、同時部署,所謂的「獨立開發部署」當場破功。

重點二:架構邊界貫穿服務、而非存在於服務之間

這是本章最核心、也最反直覺的一句:「架構邊界不在服務之間,而是貫穿服務、把它們切開。」意思是——使用案例層、實體層這些真正的架構邊界,可能同時存在於服務 A 和服務 B 內部。服務只是一個「容器/部署單位」。所以正解不是「靠拆服務來得到好架構」,而是「先在元件層級用 SOLID 把內部切乾淨(Template Method / Strategy / 插件式擴充),再決定要打包成單體還是多服務」。打包方式是部署選項,不該反過來決定你的架構。

重點三:嵌入式——「軟體不該磨損成韌體」

嵌入式世界有個陷阱叫「軟體會磨損」:程式碼跟硬體綁太緊,硬體一換、程式碼就得重寫。Uncle Bob 說,那種東西根本不叫軟體(Software),叫韌體(Firmware)。解法是嚴格分三層:純業務邏輯的軟體層(完全不知硬體存在)、跟硬體溝通的韌體層、物理硬體層;中間用 HAL(硬體抽象層)、PAL(處理器抽象層)、OSAL(OS 抽象層)隔開。最大的回報不只是「換硬體不用改軟體」,而是離線測試——大部分業務邏輯能在 PC 上跑,不必每次都「改→編譯→燒錄→重啟→測試」這個慢到崩潰的迴圈。注意:這跟同心圓是同一個靈魂——硬體、處理器、OS,全是細節。

重點四:資料庫與 Web,都只是「細節」

資料庫為什麼變成大家心中的架構核心?Uncle Bob 的回答很犀利:因為磁碟太慢。索引、B-Tree、查詢優化器、SQL,全是為了掩蓋磁碟龜速而發明的機制,跟業務邏輯一點關係都沒有。他的思想實驗很有力:如果 RAM 無限大又永不斷電,你還會寫 INSERT、拆 table、連 foreign key 嗎?不會,你會直接 users.add(newUser)、直接用資料結構。所以資料模型 ≠ 資料庫模型,業務物件該長得像業務需要的樣子,不是長得像 table。Web 同理——它跟以前的綠色終端機沒本質差別,就是個 I/O 設備;業務邏輯不該知道 HTML、HTTP header、GET/POST 是什麼。把它們都當外掛隔離,你換 UI(iOS、CLI、未來的語音/VR)時,核心一行都不用改。

注意事項

⚠️ 「資料庫是細節」是全書最具爭議的主張——理想很美,但 schema 常常就是系統最硬的約束。 Uncl Bob 要你延遲、隔離資料庫決策。但實務上的真相往往相反:在資料密集型系統裡,schema 與資料完整性約束,是整個系統最核心、最難改、活得最久的東西——資料會比你的應用程式碼活得更久。很多資深工程師會說「先把資料模型想清楚」不是壞習慣,而是抓住系統的本質。把資料庫無腦當成「可隨時替換的儲存桶」,在報表、交易一致性、複雜查詢面前,常常是天真的。我的立場:可以「隔離」資料庫存取,但別騙自己 schema 是無關緊要、隨手可換的細節。

⚠️ 「Web 是細節」對「框架就是產品本身」的團隊不成立。 對很多現代產品來說,Web/框架不是交付機制,它就是產品——互動、即時性、前端體驗本身就是價值所在。這正是 DHH(Rails)對 Uncle Bob 的核心反駁:與其把框架推到最外圈、隔著三層 adapter 跟它說話,不如「深度擁抱框架」,讓 Active Record、Controller 直接成為你的生產力來源。這是一場「理論潔癖 vs 務實擁抱框架」之爭。沒有絕對對錯——分水嶺在於:你的領域邏輯複雜度,是否大到值得跟框架保持距離?對一個 CRUD 為主的產品,全套隔離常常是淨虧損。

⚠️ 「效能也是細節」這句話要小心,別當免死金牌。 Uncle Bob 說可以先用簡單檔案、不夠快再換 MySQL、再換 Redis。理想上對,但「儲存機制可無痛替換、完全不影響業務邏輯」這個前提,在真實高負載系統裡常常站不住——你的一致性模型、交易邊界、查詢模式,會反過來滲透進設計。把效能延到最後再說,有時候是把架構債延到最痛的時候才爆。

⚠️ HAL/PAL/OSAL 抽象在資源極度受限的嵌入式場景,本身有成本。 多一層抽象就多一點 RAM、ROM、執行時間的開銷。在跑在幾 KB RAM 的 MCU 上,這個取捨是真實的——整潔分層的「離線可測」收益,要跟韌體工程師的資源預算去秤。Uncle Bob 講的方向對,但別假設它零成本。

專家補充

💡 「分散式單體」在 Uncle Bob 寫這本書(2017)時還是先知之言,現在已是業界共識。 後續整個微服務反思浪潮——Sam Newman 的《Building Microservices》、各家「我們把微服務合回單體」的事後檢討、乃至「先做模組化單體(Modular Monolith)、確定邊界穩了再拆」的主流建議——本質上都在覆述這一章:先把邏輯邊界切對,再決定物理部署。這點要大力肯定 Uncle Bob。

💡 「資料庫是細節」最務實的折衷叫 Repository 模式 + 反腐層。 你不必真的相信「DB 隨手可換」,也能拿到隔離的好處:把資料存取藏在 Repository 介面後面,讓業務邏輯依賴介面而非 SQL。換不換得掉 DB 不重要,重要的是「業務邏輯不被 SQL 細節綁架、而且好測」。這是把這一章從「教條」變成「日常可用工具」的關鍵。

💡 「無盡的鐘擺」這個歷史觀今天看格外應景。 Uncle Bob 說 UI 邏輯在 mainframe(伺服器)→ desktop(客戶端)→ Web(伺服器)→ SPA(客戶端)之間來回擺。而現在我們又看到 React Server Components、HTMX、伺服器端渲染回潮——鐘擺又往伺服器盪回去了。這正好佐證他的論點:別把架構綁死在當下這一格鐘擺位置。

討論問題

🎙️ 你做過「拆成微服務之後反而更難改」的系統嗎?回頭看,那是不是其實是個「分散式單體」——共用了 schema 或共用了資料格式?

🎙️ 對你手上的系統,「資料庫是細節」這句話成立嗎?還是 schema 其實是你最不敢動、活得最久的核心?誠實面對這題,比背誦規則重要。

🎙️ 你是「跟框架保持距離」派,還是「深度擁抱框架換生產力」派?什麼樣的專案會讓你改變立場?

🎙️ 如果你做嵌入式:你的業務邏輯能在 PC 上離線測試嗎?還是每次都得燒錄到板子上才知道對不對?那個迴圈有多痛?

更大範圍關聯

  • 承接 EP8: 上集講「依賴規則」這條鐵律,這集是把鐵律套到四個具體技術(服務/韌體/DB/Web)上——也正是套上去的瞬間,理想跟實務的張力全部浮現。

  • 鋪陳 EP10: 「Web 是細節」直接連到下集「框架是細節」——別跟框架結婚。這集對 DB 和 Web 開砲,下集對框架補最後一槍,然後用案例研究與 Simon Brown 的「遺漏的章節」收尾全書。

  • 與「模組化單體」運動: 第 27 章幾乎就是 2020 年代「Modular Monolith first, microservices later」這套主流建議的理論祖本——先切邏輯邊界,部署形式留到最後決定。

  • 與 DHH / Rails 哲學的正面對撞: 「Web/DB 是細節 vs 框架就是產品」是兩種世界觀的衝突。聽眾不必選邊站,但要聽懂雙方各自在保護什麼——一邊保護「長壽與可測」,一邊保護「速度與簡單」。

錄製建議

目標約 20 分鐘,單人主講。建議節奏:

  1. 開場 2 分鐘——用爭議句鉤住: 直接念「資料庫是細節、Web 是細節」,停頓一下,問聽眾「你聽到這句的第一反應是點頭還是翻白眼?」。預告今天有一個論點 Uncle Bob 超神準、兩個論點最該被挑戰。

  2. 吸收段 7 分鐘: 先講「微服務解耦幻覺 + 分散式單體 + 架構邊界貫穿服務」(重點一、二),語氣帶肯定——這是他最強的一章。再快速帶嵌入式三層與「軟體不該磨損成韌體」(重點三),用「離線測試」當亮點。最後鋪「DB/Web 是細節」的論證(磁碟太慢、RAM 無限大思想實驗)。

  3. 辯證段 7-8 分鐘——這集的重頭戲: 進「注意事項」。先給「微服務不自動解耦」一個大大的肯定(先見之明),製造反差後再轉折——「但同一個人,下一句講 DB 是細節,我就要踩煞車了」。完整鋪「schema 常是系統最硬約束」「框架就是產品本身(DHH 反駁)」「效能是細節別當免死金牌」。落點:用 Repository 模式當折衷,把教條降溫成工具。

  4. 收尾 3 分鐘: 一句話收——「邊界是邏輯的、不是物理的」,並預告 EP10「最後一集:框架是細節、一個完整案例研究,還有一個被偷偷塞進書末、其實在打臉前面的『遺漏的章節』,加上全 10 集大收束」。

語速策略:肯定段落講得篤定有力;辯證段落刻意放慢、用「我跟你說實話」的語氣,這是恩普拉氏吸收+辯證的招牌。爭議句可以重複念兩次,讓聽眾自己在心裡選邊。