遺漏的章節 (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)**來隱藏實作細節。 讓編譯器成為你的架構守門員。