遺漏的章節 (The Missing Chapter)#
我們在討論架構時,往往專注於高層次的元件劃分(如 UI, Interactor, Database)。但當我們真正打開 IDE 看程式碼時,目錄結構往往又是另一回事。 Simon Brown 在這一章提出了一個尖銳的問題:「你的目錄結構(Packaging)是否反映了你的架構設計?」
他以一個「線上書店」為例,比較了四種程式碼組織策略。
一、逐層打包 (Package by Layer)#
這是最傳統、最常見的 Java/Web 專案分法。我們依照技術角色來進行水平分層。
- 結構:
controllers/,services/,repositories/,models/。 - 優點: 簡單,好理解。
- 缺點:
- 無法尖叫: 看目錄結構看不出這是賣書的還是賣漢堡的。
- 封裝洩漏: 為了讓 Controller 呼叫 Service,Service 必須是
public;為了讓 Service 呼叫 Repository,Repository 必須是public。結果就是,任何人都可以繞過 Service 直接呼叫 Repository。
二、依功能打包 (Package by Feature)#
這是一種垂直切分的方法。我們依照領域概念來分組。
- 結構:
orders/,books/,customers/。 - 內容:
orders包裡面同時包含OrdersController,OrdersService,OrdersRepository。 - 優點: 符合「尖叫架構」,一眼看出業務領域。
- 缺點: 如果沒有嚴格控制存取權限,它依然只是一種視覺上的分組,依賴關係可能還是亂七八糟。
三、依元件打包 (Package by Component)#
這是作者最推薦的方法,結合了上述兩者的優點,並強調**「粗粒度元件」**的概念。
- 核心思想: 將與單個粗粒度元件(如「訂單處理」)有關的所有責任,綑綁到單一包中。
- 結構:
- 建立一個
OrdersComponent包。 - 對外: 只有一個
OrdersService(介面) 是public的。 - 對內:
OrdersRepository,OrdersController,OrdersImplementation都是package-private(Java 預設) 的。
- 建立一個
- 優點:
- 真正的封裝: 外部程式碼根本「看得到 Service 但看不到 Repository」。
- 安全性: 強制所有對訂單的操作都必須經過 Service,無法繞過業務邏輯直接存取資料庫。
- 整潔: 業務邏輯與持久性程式碼在元件內部是分離的,但對外部而言,它們是一個整體的元件。
四、魔鬼藏在細節中 (The Devil is in the Details)#
這四種分法(包含 Port & Adapters),如果所有的類別都設為 public,那麼它們在編譯器眼中其實是一模一樣的。
- 架構的幻覺: 如果你畫了嚴格的層級圖,但在程式碼中把所有類別都設為
public,那你只是在自我催眠。開發者隨時可以寫出跨層級的依賴,編譯器不會阻止他們。 - 編譯器的力量: 架構師應該善用語言特性(如 Java 的
package-private, C# 的internal)來強制執行架構風格。
總結: 不要讓你的架構圖變成紙上談兵。 選擇**「依元件打包」,並利用存取修飾符(Access Modifiers)**來隱藏實作細節。 讓編譯器成為你的架構守門員。