Reuse Patterns#
在單體架構中,程式碼重用只是匯入或注入共用類別檔案的問題。然而在分散式架構中,如何處理共用功能變得更加複雜。本章介紹四種管理程式碼重用的技術:Code Replication、Shared Library、Shared Service,以及 Sidecar / Service Mesh,並分析各自的優缺點與適用場景。

Figure 8.1: Code reuse is a hard part of distributed architecture
Code Replication#
- 概念:將共用程式碼複製到每個服務的原始碼倉庫中,完全避免程式碼共享
- 在微服務早期盛行,源自 bounded context 概念與「share nothing architecture」的理念
- 適用場景:程式碼是靜態的、不太會變動的簡單工具類別(如 annotations、attributes、簡單的 utility)
- 從單體遷移至分散式架構時,可用於複製通用靜態工具類別(如
Utility.cs),讓各服務可依需求客製化

Figure 8.2: With replication, shared functionality is copied into each service
若共用程式碼中發現 bug 或需要修改,必須逐一更新所有複製該程式碼的服務,非常困難且耗時。
Trade-Offs:
| 優勢 | 劣勢 |
|---|---|
| 保留 bounded context | 難以套用程式碼變更 |
| 無程式碼共享依賴 | 服務間程式碼不一致 |
| 無版本控制能力 |
Shared Library#
- 概念:將共用程式碼打包為外部製品(如 JAR、DLL),在編譯時期綁定到各服務
- 看似簡單直覺,但涉及共用函式庫粒度(granularity)與版本控制(versioning)兩大挑戰

Figure 8.3: With the shared library technique, common code is consolidated and shared
Dependency Management and Change Control#
共用函式庫的粒度涉及兩股對立力量:
- 粗粒度(Coarse-grained):單一大型函式庫,依賴管理簡單,但任何變更都會影響所有服務,強制不必要的重新測試與部署
- 細粒度(Fine-grained):多個小型函式庫(如 security、formatters、calculators),變更影響範圍小,但依賴關係矩陣變得極度複雜,形成所謂的 distributed monolith

Figure 8.4: Changes to coarse-grained shared libraries impact multiple services

Figure 8.5: Changes to fine-grained shared libraries impact fewer services
建議避免大型粗粒度的共用函式庫,盡量使用較小的、按功能分區的函式庫,優先考慮 change control 而非 dependency management。
Versioning Strategies#
- 務必使用版本控制(always use versioning),提供向後相容性與敏捷性
- 避免使用
LATEST版本,以防止熱部署時引入不相容的變更 - 版本溝通困難:在多團隊的大型分散式架構中,版本變更資訊難以有效傳遞
- 版本棄用策略(Deprecation strategies):
- Custom:每個函式庫依其變更頻率設定不同的版本數量(如穩定的 Security.jar 只保留 2-3 版,頻繁變更的 Calculators.jar 保留 10 版)
- Global:所有函式庫統一最大向後相容版本數,管理簡單但可能導致大量不必要的 churn
Trade-Offs:
| 優勢 | 劣勢 |
|---|---|
| 可進行版本變更 | 依賴管理困難 |
| 編譯時期綁定,降低執行時期錯誤 | 異質代碼庫中程式碼重複 |
| 共用程式碼變更的敏捷性好 | 版本棄用困難 |
| 版本溝通困難 |
When To Use#
- 適用於同質環境(homogeneous),共用程式碼變更頻率低到中等
- 由於在編譯時期綁定,不影響 performance、scalability、fault tolerance 等營運特性
Shared Service#
- 概念:將共用功能放在獨立部署的服務中,透過組合(composition)而非繼承(inheritance)實現重用
- 變更共用功能無需重新部署使用該功能的服務

Figure 8.6: With the shared service technique, common functionality is made available
Change Risk#
- 優勢:變更隔離在共用服務中,其他服務無需重新部署
- 風險:共用服務的變更是執行時期(runtime)變更,不當修改可能導致所有依賴服務同時故障
- API endpoint 版本控制比 shared library 的版本控制更複雜且主觀,特別是涉及多種通訊協議(REST、gRPC、messaging)時

Figure 8.7: Shared functionality changes are isolated to only the shared service

Figure 8.8: Changes to a shared service can break other services at runtime
Performance#
- 存取共用服務需透過網路呼叫,引入 network latency 與 security latency
- 可使用 gRPC 降低延遲,或使用非同步訊息(messaging)來緩解效能問題

Figure 8.9: Shared service introduces network and security latency
Scalability#
- 共用服務必須隨著使用它的服務一起擴展,管理上更複雜

Figure 8.10: Shared services must scale as dependent services scale
Fault Tolerance#
- 若共用服務不可用,所有依賴服務也將無法運作
- Shared library 不存在此問題,因為共用功能在編譯時期已嵌入服務

Figure 8.11: Shared services introduce fault-tolerance issues
Trade-Offs:
| 優勢 | 劣勢 |
|---|---|
| 適合高度變動的程式碼 | 版本控制困難 |
| 異質代碼庫中無程式碼重複 | 因延遲影響效能 |
| 保留 bounded context | 容錯與可用性問題(服務依賴) |
| 無靜態程式碼共享 | 擴展性與吞吐量問題(服務依賴) |
| 執行時期變更風險較高 |
When to Use#
- 適合高度多語言環境(polyglot)且共用功能經常變動的場景
- 需注意執行時期副作用與風險
Sidecars and Service Mesh#
- Sidecar pattern 源自 hexagonal architecture(Ports and Adaptors Pattern),將領域邏輯與技術基礎設施邏輯解耦

Figure 8.12: The Hexagonal pattern separated domain logic from technical coupling
- 將 operational concerns(如 logging、monitoring、circuit breaker、service discovery、authentication、authorization)抽離到 sidecar 元件中

Figure 8.13: Two microservices that share the same operational capabilities

Figure 8.14: When each microservice includes a common component, architects can extract into sidecar
- 當每個服務都包含 sidecar 元件時,形成 service mesh,提供一致的 operational interface 與 service plane

Figure 8.15: A service mesh is an operational link among services
- 屬於 orthogonal reuse pattern,解決 domain coupling 與 operational coupling 正交交叉的問題
Sidecar 僅用於 operational coupling(logging、monitoring、service discovery 等),不應放入 domain 元件(如
Address或Customerclass),以避免不適當的耦合。
Trade-Offs:
| 優勢 | 劣勢 |
|---|---|
| 提供一致的方式建立隔離的耦合 | 每個平台需實作各自的 sidecar |
| 允許一致的基礎設施協調 | sidecar 元件可能變得龐大複雜 |
| 可由團隊、集中或混合方式擁有 |
When to Use#
- 適合在分散式架構中傳播 cross-cutting concern
- 提供類似 Gang of Four Decorator Design Pattern 的架構等效物,獨立於正常連接性之外「裝飾」行為
Sysops Squad Saga: Common Infrastructure Logic#
團隊決定使用 sidecar 與 service mesh 來統一 operational coupling:
- 由共用基礎設施團隊擁有並維護 sidecar
- 提供的服務包括:Monitoring、Logging、Service discovery、Authentication、Authorization
- 判斷規則:若超過一半的團隊需要某個共用工具程式碼,就放入 sidecar;否則暫不放入
Code Reuse: When Does It Add Value?#
- Reuse 是最容易被濫用的抽象之一,過度重用會導致架構脆弱性(brittleness)
- 早期 SOA 追求最大化重用的教訓:將所有 domain 的 Customer 資訊統一到單一服務,反而造成架構災難

Figure 8.16: Each domain within a large insurance company has a view of the customer

Figure 8.17: Unifying on a centralized Customer service
- 重用有兩個面向:
- 抽象(abstraction):識別重用候選者
- 變更速率(rate of change):決定重用是否真正有價值
Reuse is derived via abstraction but operationalized by slow rate of change. 我們從作業系統、開源框架等變更緩慢的資產中受益於技術耦合,但內部 domain 能力或快速變動的技術框架則是糟糕的耦合目標。
Reuse via Platforms#
- Platform 是組織內重用的新目標:為每個可區分的 domain capability 建構一個具有良好定義 API 的平台
- 緩慢的變更速率驅動此推理:API 可設計為與呼叫者鬆散耦合,允許內部積極的實作變更而不破壞 API
Sysops Squad Saga: Shared Domain Functionality#
團隊面臨 ticketing 領域的三個服務(Ticket Creation、Ticket Assignment、Ticket Completion)共用資料庫邏輯的問題:
- Shared data service 方案:提供資料抽象與集中連線池,但帶來效能、容錯與 runtime 變更風險

Figure 8.18: Option using a shared Ticket Data service for common database logic
- Shared library (DLL) 方案:消除服務依賴和 HTTP 流量,但變更需重新測試部署

Figure 8.19: Option using a shared library for common database logic
最終團隊選擇 shared library,因為分析歷史程式碼變更記錄後發現共用資料庫邏輯相當穩定,不常變更。透過版本控制提供向後相容性,降低變更風險。