第 1 / 6 部 待錄

可維護性與分層架構的原罪

《整潔架構實戰》Podcast 準備稿:可維護性與分層架構的原罪

書名: 整潔架構實戰(Get Your Hands Dirty on Clean Architecture) 作者: Tom Hombergs 系列: 設計品味系列 公開標題建議: 設計品味系列|整潔架構實戰.第一集:為什麼分層架構越寫越爛? (1/6) 涵蓋範圍: 第 1 章「可維護性」、第 2 章「分層架構的問題」

背景速覽

這是「設計品味系列」要帶大家動手做的第一本書。如果說 Uncle Bob 的《Clean Architecture》是把架構的「世界觀」講透的那本聖經,那麼這本《整潔架構實戰》就是那本聖經的「實作手冊」——作者 Tom Hombergs 拿一個 Java + Spring 的轉帳小應用 BuckPal,一行一行示範六邊形/埠與轉接器架構到底怎麼落地。全書 15 章,我打算拆成大約 6 集走完。

今天這兩章是整本書的動機地基。第 1 章先回答一個大哉問:在一堆品質需求裡,為什麼作者獨獨挑「可維護性」當主角?第 2 章則拿出一個幾乎每個工程師都寫過的東西開刀——傳統三層架構,告訴你它為什麼會隨時間爛掉。先把痛點講清楚,後面幾集的「解藥」才有意義。

一句話重點

軟體真正該優先設計的品質需求是「可維護性」——而我們最熟悉、最自然的那套分層架構,恰恰是最容易在不知不覺間侵蝕可維護性的結構。

值得討論的重點

重點一:在所有品質需求裡,可維護性是那個「樞紐」

軟體需求分兩種:功能性需求(軟體要做什麼)和品質需求(又叫非功能性需求——擴展性、安全性、效能、可靠性那一長串)。架構師的工作就是針對最重要的品質需求去設計。高吞吐量交易系統押注擴展性,處理個資的系統押注安全性。

那作者為什麼說可維護性特別?因為它有「樞紐地位」:軟體可維護,代表它容易改;容易改,就代表它有彈性、夠模組化、改動便宜;而一旦它容易改,需要更快、更安全、更高效能的時候,你就能把它演進成那個樣子。換句話說,可維護性是其他所有品質的「前置條件」——你可以從一個好改的系統長出任何東西,但很難從一個改不動的系統長出任何東西。

作者自己很誠實,說這個推論有點「先射箭再畫靶」,刻意把所有品質都連回可維護性。但核心是站得住腳的:軟體生命週期裡「改變」幾乎是必然,而可維護性就是你應付改變的本錢。

重點二:可維護性不只是省錢,它直接影響團隊的和諧與留任

這章有個很容易被略過、但我覺得很值得講的論點:可維護性差,會製造「人」的衝突。

邏輯是這樣的:可維護性差 → 每次改動成本越來越高 → 產品端開始抱怨「怎麼改個東西要這麼久」、工程端則辯解「交付功能本來就比改善架構優先」→ 雙方對立。所以可維護性跟變更成本成反比,也就跟團隊衝突的機率成反比。投資可維護性,其實是在投資團隊的和諧。

作者還引了一個我很喜歡的概念叫「開發者喜悅(developer joy)」。可維護的系統 → 改動更快(生產力高)、過程更順(喜悅高)→ 喜悅又帶來人才留任。他還把這個對應到 SPACE 生產力框架裡的「S」(滿意度與幸福感)。所以這章其實在說一件事:架構不是純技術議題,它是會傳導到「人會不會想留在這個團隊」的議題。

重點三:分層架構的原罪——它讓你不知不覺把資料庫當成世界的中心

第 2 章是今天的主菜,也是全書真正的起點。作者先講公道話:分層架構本身是一個紮實的模式,用得好一樣可維護。但他的經驗是——它對變更太脆弱,因為它給的護欄太少,全靠人的紀律撐著。

最核心的一個原罪:它助長以資料庫為中心的設計。傳統三層的依賴方向是 Web → 領域 → 持久化 → 資料庫,一切都疊在資料庫之上。作者反問得很犀利:你最近實作一個功能,是先想領域邏輯,還是先想資料表長怎樣?多數人是先畫資料表。但這從業務角度毫無道理——我們真正在建模的是「行為」(改變狀態的規則),不是「狀態」本身。順著分層的依賴方向,你會很自然地先設計資料庫,再把業務邏輯堆在上面,本末倒置。

ORM 還會火上加油:ORM 管理的 entity 屬於持久化層,但因為上層能用下層,領域層就被「允許」直接拿這些 entity 當業務模型——只要被允許,遲早就會用。結果領域邏輯和持久化邏輯黏成一團,改一個就得動另一個。Martin Fowler 在《重構》裡管這個壞味道叫「發散式變更」。

重點四:捷徑、跳層、過寬服務——分層如何一步步腐蝕

除了資料庫中心,作者列了一串分層架構的「慢性病」,每一個老工程師讀了都會點頭:

  • 容易抄捷徑:分層唯一的全域規則是「只能存取同層或更下層」。所以你想用上層的東西?把它「往下推一層」就合法了。問題是這麼做一次,就替第二次開了門——「別人能這樣,我為什麼不行」。這就是破窗效應(第 11 章還會深入)。久了最底層的持久化層就一直長胖。
  • 越來越難測:想改一個欄位,從 Web 層直接戳持久化層、跳過領域層,前幾次感覺很爽。但領域邏輯就這樣散到 Web 層,而且 Web 層的測試現在得同時 mock 領域層和持久化層——當「搞懂依賴、建 mock」比「寫測試本身」還累,大家就乾脆不寫測試了。
  • 隱藏使用案例:分層不限制服務的「寬度」,於是冒出服務多個使用案例的「過寬服務」(broad service),又難測、又難找,還讓平行開發卡死(大家同時改同一個服務,瘋狂合併衝突)。作者的解法預告很簡單:與其在 UserService 裡翻註冊邏輯,不如直接開 RegisterUserService

注意事項

⚠️ 「可維護性是最重要的品質需求」這個主張,要小心別變成教條。作者自己都承認推論有「先射箭再畫靶」的味道。現實裡有些系統,可靠性或安全性才是真正壓倒一切的(想想航太、醫療、金流核心),這時你優先設計的不該是「好改」,而是「不會出錯」。更務實的版本是:可維護性是個極好的「預設優先項」,但不是唯一的王。當你的領域有更硬的非功能性需求時,它得讓位。

⚠️ 作者把分層架構講得像個慢性病患者,但要公道地說:他批評的其實是「沒有額外紀律與工具約束的分層」。一個有 ArchUnit 把關、有 code review 紀律、團隊規模小的分層架構,可以活得很健康。本書的賣點不是「分層一無是處」,而是「分層的護欄不夠、太依賴人的自律,而人的自律會在 deadline 和人員輪替時崩潰」。聽眾要帶走的是「結構性護欄 > 人的紀律」,而不是「分層 = 爛」。

⚠️ 「先做領域邏輯、再做資料庫」聽起來很正確,但對純 CRUD 應用其實是過度設計。如果你的應用 80% 就是把表單存進資料庫、再撈出來顯示,那「以資料庫為中心」反而是最誠實、最省事的設計。作者在第 15 章自己也承認這點。所以這條原則的適用條件是:你的領域邏輯真的夠複雜、夠重要。沒有複雜領域,硬要把資料庫推到外圈,只是替自己找麻煩。

專家補充

💡 這本書和 Uncle Bob 的《Clean Architecture》是「理論 vs 落地」的完美一對。Uncle Bob 給你那張著名的同心圓和「依賴規則」,但他刻意寫得語言無關、偏抽象,讀完你常會問「那 Java 到底怎麼寫?」。Tom Hombergs 這本就是來補這一刀的——他直接給你套件結構、給你 SendMoneyService 的程式碼、給你 Spring 標註。可以這樣跟聽眾定位:想要世界觀,讀 Uncle Bob;想要這週就能套進專案,讀這本。

💡 「以資料庫為中心」這個批評,背後其實是 Uncle Bob「資料庫是細節(detail)」這個更激進主張的溫和版。Uncle Bob 會說資料庫跟 Web framework 一樣,都只是插在外圈的「I/O 裝置」,你的業務規則根本不該知道它存在。Tom 沒講得這麼極端,但「持久化是領域的 plugin」這個說法,下一集就會正式登場。

💡 補一個現代脈絡:作者談的「過寬服務」「發散式變更」,在微服務時代有了新的對應——一個邊界劃錯的微服務,就是一個分散式的過寬服務,而且還多了網路延遲和分散式交易的痛。所以這本書雖然在講單體(monolith)內部的架構,但它教你「怎麼劃乾淨的邊界」這件事,恰恰是你日後要不要、以及如何拆微服務的前置功課。邊界劃不好,拆成微服務只會把痛苦放大。

討論問題

🎙️ 回想你手上那個「改一個小功能卻要動到一大片」的專案——它的依賴方向,是不是最後都疊在資料庫上?你當初實作功能時,是先想領域邏輯,還是先開 schema 設計工具?

🎙️ 「可維護性差會製造團隊衝突」——你有沒有親身經歷過產品端嫌你慢、工程端覺得不被理解的那種對立?回頭看,那次的根因是不是其實是架構爛、改不動?

🎙️ 你的程式庫裡有沒有一個「什麼都管」的過寬服務(那種上千行、誰都不敢動的 God Service)?它是怎麼一步步長成這樣的?第一個往裡面塞東西的人,是不是就是在「抄捷徑」?

🎙️ 誠實面對:你現在的應用,領域邏輯真的複雜到值得用這本書的架構嗎?還是它其實大半是 CRUD、以資料庫為中心反而最誠實?

更大範圍關聯

  • 接 Uncle Bob《Clean Architecture》:那本給你「依賴規則」和同心圓的世界觀,這本給你 Java/Spring 的具體落地。本系列可以把這本當成那本的「實作配套」,兩本對照著讀最有收穫。

  • 接 Martin Fowler《重構》:第 2 章直接引用了 Fowler 的「發散式變更」壞味道。Fowler 教你怎麼識別與消除壞味道(戰術層),這本教你用架構從源頭預防壞味道滋生(戰略層)。

  • 接 Alistair Cockburn 的六邊形架構(埠與轉接器):今天講的所有分層痛點,下一集就會用 Cockburn 的六邊形 + Uncle Bob 的依賴反轉來一次解決。今天先把病講清楚,下集開藥。

  • 接 DDD(領域驅動設計):作者反覆強調「先做領域、把領域放中心」,這正是 DDD 的精神。可以預告:這本書其實是「讓 DDD 能落地的架構容器」,第 15 章作者會說沒有這種架構,DDD 根本做不純。

錄製建議

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

  • 0–3 分:系列定位 + 本書定位開場。一句話交代「設計品味系列第一本,這是 Uncle Bob 那本《Clean Architecture》的 Java 落地手冊,全書約 6 集,今天先講動機」。
  • 3–8 分:第 1 章。從「可維護性是樞紐」切入,再帶「它影響的不只是成本,是團隊和諧與留任」——用「產品嫌你慢、工程覺得委屈」這個共感場景開場最有畫面。
  • 8–15 分:第 2 章,本集主菜。先講「以資料庫為中心」這個原罪(用「你是先想領域還是先畫 schema」這個問題勾住聽眾),再快速掃過捷徑/跳層/過寬服務三個慢性病,每個都用「你一定遇過」的語氣。
  • 15–18 分:辯證時間。誠實校準——分層不是爛,是護欄不夠;以資料庫為中心對純 CRUD 反而誠實。把「結構護欄 > 人的紀律」這句金句留給聽眾。
  • 18–20 分:收尾 + 預告。預告下一集進入解藥——依賴反轉、六邊形架構,以及那個能「用程式碼喊出架構」的套件結構。

口氣提醒:這集是「講病」,多用「你一定寫過那種改不動的分層 App」的共感開場,讓聽眾先痛起來,下一集的藥才會甜。