邊界剖析 (Boundary Anatomy)#

軟體架構中的邊界(Boundaries)並非只有一種形式。它們可以發生在不同的層次,從編譯期的原始碼隔離,到執行期的網路隔離。 一個複雜的系統往往會混合使用多種邊界策略。

一、跨越邊界的機制:多型的魔法#

在討論不同形式之前,必須先理解「跨越邊界」的核心機制。 無論邊界是透過函式呼叫還是網路封包,管理的重點都在於:原始碼的依賴關係

我們利用**動態多型(Dynamic Polymorphism)**來管理這種關係:

  1. 一般情況(Low $\rightarrow$ High):
    • 低層級客戶端呼叫高層級服務。
    • 執行期(Runtime)方向與編譯期(Compile-time)依賴方向相同
  2. 關鍵架構(High $\rightarrow$ Low):
    • 高層級業務邏輯需要呼叫低層級細節。
    • 依賴反轉: 透過介面多型,執行期控制流是 High -% Low,但編譯期依賴方向被反轉為 Low -% High(低層實作依賴高層介面)。

二、四種邊界策略#

Uncle Bob 分析了四種從「緊密」到「鬆散」的邊界形式:

1. 單片 (Monolith) - 原始碼層級#

這是最常見的形式。

  • 定義: 所有的函數與類別都在同一個處理器同一個位址空間中運作。
  • 通訊: 簡單的函式呼叫。速度快、成本低。
  • 解耦方式: 雖然物理上在一起,但在邏輯上透過原始碼層級區分。高層元件獨立於低層細節,透過介面互動。

2. 部署元件 (Deployment Components) - 二進位層級#

  • 定義: 以可獨立部署的二進位檔(.jar, .dll, .gem)為邊界。
  • 通訊: 透過**動態鏈結(Dynamic Linking)**進行函式呼叫。
  • 特點: 不涉及重新編譯。開發者可以抽換某個 .dll,而無需重建整個系統。這是一種「部署層級」的解耦。

3. 本地行程 (Local Processes)#

  • 定義:同一個處理器,但在不同位址空間中運作。
  • 通訊: 透過 Socket、共享記憶體或 OS 訊號(IPC)。
  • 視角: 可視為一個「超級元件」。
  • 限制: 高層級行程的原始碼,絕不能包含低層級行程的具體知識(如 Process ID、實體位址)。它們應該透過配置或註冊表來尋找彼此。

4. 服務 (Services)#

  • 定義: 最強的邊界。通常在不同主機上運行。
  • 通訊: 網路封包。速度較慢,需考慮延遲問題。
  • 外掛概念: 即便是在服務層級,我們依然應用 Plugin 架構
    • 錯誤觀念: 認為服務就是把功能切開就好。
    • 正確觀念: 低層級服務應該被視為「插入」到高層級服務中的外掛。高層服務不應包含低層服務的具體知識。

總結: 管理程式運行時的跨越邊界(跨端呼叫函式),本質上就是在管理原始碼的依賴關係。 這關乎當某個模組改變時,哪些相依模組需要更改或重新編譯。良好的架構會讓系統邊界混合使用這些策略(本地通訊 + 網路邊界),以達到最佳的效能與維護性平衡。