邊界剖析 (Boundary Anatomy)#
軟體架構中的邊界(Boundaries)並非只有一種形式。它們可以發生在不同的層次,從編譯期的原始碼隔離,到執行期的網路隔離。 一個複雜的系統往往會混合使用多種邊界策略。
一、跨越邊界的機制:多型的魔法#
在討論不同形式之前,必須先理解「跨越邊界」的核心機制。 無論邊界是透過函式呼叫還是網路封包,管理的重點都在於:原始碼的依賴關係。
我們利用**動態多型(Dynamic Polymorphism)**來管理這種關係:
- 一般情況(Low $\rightarrow$ High):
- 低層級客戶端呼叫高層級服務。
- 執行期(Runtime)方向與編譯期(Compile-time)依賴方向相同。
- 關鍵架構(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 架構。
- 錯誤觀念: 認為服務就是把功能切開就好。
- 正確觀念: 低層級服務應該被視為「插入」到高層級服務中的外掛。高層服務不應包含低層服務的具體知識。
總結: 管理程式運行時的跨越邊界(跨端呼叫函式),本質上就是在管理原始碼的依賴關係。 這關乎當某個模組改變時,哪些相依模組需要更改或重新編譯。良好的架構會讓系統邊界混合使用這些策略(本地通訊 + 網路邊界),以達到最佳的效能與維護性平衡。