服務:偉大與微小 (Services: Great and Small)#
近年來,「服務導向架構(SOA)」與「微服務(Microservices)」變得極為流行。人們普遍認為服務架構能帶來強大的解耦與獨立性。 但 Uncle Bob 在這一章潑了一盆冷水:服務(Service)只是一種物理上的運作機制,而非邏輯上的架構邊界。
一、服務架構的兩大迷思#
1. 解耦的幻覺 (The Decoupling Fallacy)#
- 迷思: 因為服務在不同的行程(Process)或伺服器上執行,透過網路通訊,所以它們彼此是嚴格解耦的。
- 現實: 服務之間往往透過共享資料(Shared Data) 產生強烈的耦合。
- 如果你修改了服務間傳遞的 JSON 資料欄位,或更糟的是,多個服務共享同一個資料庫 Schema,那麼它們就完全沒有解耦。
- 這種「邏輯上的耦合」遠比「物理上的分離」更重要。
2. 獨立開發部署的幻覺 (The Independent Development/Deployment Fallacy)#
- 迷思: 將系統拆成小服務,就能讓團隊獨立開發、部署。
- 現實: 這只有在服務邊界與架構邊界一致時才成立。
- 橫切關注面(Cross-Cutting Concerns): 如果一個新功能(例如:Uber 新增「載運寵物」服務)需要同時修改 UI 服務、計費服務、司機媒合服務,那麼這些團隊就無法獨立行動。他們必須協調、同時部署,這就是所謂的「分散式單體(Distributed Monolith)」。
二、物件導向的救贖:多型#
要解決上述問題,我們不能依賴「服務」這種物理邊界,而必須回歸「元件」設計原則。
1. 服務內部的元件化#
我們不該把服務寫成一整塊程式碼(Monolith)。 服務內部應該由遵循依賴規則的元件組成。我們可以利用 Template Method 或 Strategy 模式,創造出可擴展的結構。
2. 符合 OCP 的擴展#
如果架構設計得當(基於 SOLID 原則),新增功能(如「載運寵物」)應該只需要:
- 建立新的元件(Jar/Dll)。
- 擴展既有的抽象類別。
- 將新元件加入服務的載入路徑。
- 結果: 我們不需要修改舊的服務程式碼,而是以插件(Plugin) 的方式擴充服務。
三、架構邊界 vs. 服務邊界#
這是本章最核心的觀念: 「架構邊界不在服務之間,而是貫穿服務並分解它們。」
- 錯誤的觀點: 服務 A 是邊界,服務 B 是另一個邊界。
- 正確的觀點:
- 架構邊界(如:使用案例層、實體層)可能會同時存在於服務 A 和服務 B 之中。
- 服務只是一個容器。
- 為了處理由橫切關注面帶來的問題,我們必須將服務設計成由內部元件組成。
總結: 架構是由邏輯邊界(依賴反轉、穩定的介面)定義的,而不是由物理機制(服務通訊方式)定義的。 你可以選擇將元件打包成單一程式(Monolith),也可以打包成多個服務(Microservices),這只是部署選項的差異,而不應影響核心架構的設計。