Service Granularity#
決定服務的適當粒度(granularity)– 即服務的大小 – 是軟體架構中最困難的部分之一。架構師和開發團隊經常為此掙扎。
Modularity 與 Granularity 是不同的概念:
- Modularity(模組化):關注將系統拆分為獨立部分
- Granularity(粒度):關注這些獨立部分的大小
分散式系統中的大多數問題和挑戰通常與粒度有關,而非模組化。
衡量服務粒度的方式:
- 服務的粒度不由類別數量或程式碼行數定義,而是由服務做什麼來決定
- 一個客觀的衡量指標是計算服務中的 statements 數量
- 另一個指標是衡量服務暴露的 public interfaces 或 operations 數量
兩股相反的力量決定了服務粒度:granularity disintegrators(拆分驅動力)和 granularity integrators(整合驅動力)。達到適當粒度的秘訣在於在這兩股力量之間取得平衡。

Figure 7.1: Service granularity depends on a balance of disintegrators and integrators
常見的錯誤是只關注 granularity disintegrators 而忽略 granularity integrators。
Granularity Disintegrators#
Granularity disintegrators 回答的問題是:「何時應該考慮將服務拆分為更小的部分?」
Service Scope and Function(服務範圍與功能)#
- 這是最常被引用但也最主觀的拆分理由
- Single-responsibility principle 常被用作依據,但不同人對「單一職責」有不同理解
- 例如:通知服務可以是一個服務(「通知」本身是單一職責),也可以拆為三個(SMS、email、postal letter)
- 判斷方式: 檢視服務中的功能是否內聚(cohesive),即功能之間是否高度相關且互相協作
- Cohesion(內聚性)比 single-responsibility principle 更客觀
- 若服務包含不相關的功能(如客戶註冊和付款處理),則應考慮拆分

Figure 7.2: A service with relatively strong cohesion is not a good candidate for disintegration

Figure 7.3: A service with relatively weak cohesion is a good candidate for disintegration
Code Volatility(程式碼變動性)#
- 當服務中某個元件的變更頻率遠高於其他元件時,應考慮拆分
- 頻繁變更的元件每次變更都會導致整個服務需要重新測試和部署
- 拆分後可降低測試範圍和部署風險

Figure 7.4: An area of high code change in a service is a good candidate for disintegration
Scalability and Throughput(可擴展性與吞吐量)#
- 當服務中某個功能的擴展需求遠高於其他功能時,應考慮拆分
- 例如:付款處理需要大量擴展,但付款歷史查詢不需要
- 拆分後可針對不同功能獨立擴展

Figure 7.5: Differing scalability and throughput needs is a good disintegration driver
Fault Tolerance(容錯性)#
- 當某個功能的失敗不應導致整個服務不可用時,應考慮拆分
- 例如:訂單歷史查詢失敗不應影響新訂單的處理
- 拆分可提高整體系統的可用性

Figure 7.6: Fault tolerance and service availability are good disintegration drivers
Security(安全性)#
- 當服務中某些功能需要更嚴格的安全控制時,應考慮拆分
- 例如:付款處理需要 PCI 合規,而付款歷史查詢不需要
- 拆分可讓安全控制更精確,減少安全稽核的範圍

Figure 7.7: Security and data access are good disintegration drivers
Extensibility(可擴展性)#
- 當服務需要頻繁新增功能且這些新功能與現有功能獨立時,應考慮拆分
- 拆分後可獨立擴展功能,不影響現有服務

Figure 7.8: Planned extensibility is a good disintegration driver
Granularity Integrators#
Granularity integrators 回答的問題是:「何時應該考慮將服務合併在一起?」
Database Transactions(資料庫交易)#
- 當兩個服務需要參與同一個 ACID transaction 時,應考慮合併
- 分散式交易(如 two-phase commit)在分散式架構中通常不可行且不可靠
- 若必須保證資料一致性,最好將這些功能保持在同一服務中
- 如果使用 eventual consistency 可以接受,則仍可拆分

Figure 7.9: Separate services with atomic operations have better security access control

Figure 7.10: Separate services with combined operations do not support database transactions

Figure 7.11: A single service supports database (ACID) transactions
優先選擇避免分散式交易,而非嘗試實現它們。如果兩個操作必須在同一個 ACID 交易中完成,就將它們放在同一個服務中。
Workflow and Choreography(工作流程與編排)#
- 當拆分服務會產生大量的服務間通訊(inter-service communication)時,應考慮合併
- 過多的 orchestration 或 choreography 會增加:
- 網路延遲
- 可靠性問題(更多網路呼叫 = 更多失敗點)
- 複雜性(錯誤處理、超時、重試等)
- 若兩個服務幾乎總是一起被呼叫,合併可能是更好的選擇

Figure 7.12: Too much workflow impacts fault tolerance

Figure 7.13: Too much workflow impacts overall performance and responsiveness

Figure 7.14: Too much workflow impacts reliability and data integrity
Shared Code(共用程式碼)#
- 當兩個服務共享大量程式碼且這些程式碼頻繁變更時,應考慮合併
- 共用程式碼的處理方式:
- 共用函式庫(shared library):適合基礎設施級的共用程式碼
- 共用服務(shared service):適合領域級的共用程式碼
- 合併服務:當共用程式碼量太大且變更頻繁時
- 若共用程式碼穩定且不常變更,則可安全地使用共用函式庫,不需要合併

Figure 7.15: A change in shared code requires a coordinated change to all services
Data Relationships(資料關係)#
- 當兩個服務需要頻繁交叉存取對方的資料時,應考慮合併
- 跨服務的資料存取需要額外的服務間呼叫,增加延遲和複雜性
- 若兩個服務的資料高度相關且經常一起使用,保持在同一服務中可簡化資料存取

Figure 7.16: The database table relationships of a consolidated service

Figure 7.17: Database table relationships impact service granularity
Finding the Right Balance#
- 達到正確的服務粒度需要在 disintegrators 和 integrators 之間找到平衡
- 沒有統一的正確答案,取決於具體的業務需求和技術約束
- 建議的做法:
- 列出所有適用的 disintegrators(拆分的理由)
- 列出所有適用的 integrators(合併的理由)
- 客觀地權衡兩側的力量
- 做出決定並記錄在 ADR 中
使用 granularity disintegrators 和 integrators 的框架可以移除主觀意見和直覺,客觀地分析 trade-offs,為是否拆分服務提供堅實的理由。
Sysops Squad Saga: Ticket Assignment / Customer Registration Granularity#
Ticket Assignment 粒度分析:
- Taylen(技術主管)主張將核心工單功能(ticket creation、completion、expert assignment、expert routing)拆為四個獨立服務,認為「micro 就是要小」
- Logan 指出並非所有部分都必須是微服務,這是微服務架構風格中最大的陷阱之一
分析 Ticket Assignment 的 disintegrators:
- 核心工單功能之間高度內聚,不應拆分
- 程式碼變動性相似,不需因此拆分
分析 Ticket Assignment 的 integrators:
工單建立和分配之間需要資料庫交易保證
拆分會導致大量的服務間通訊
最終決定:核心工單功能保持為單一服務

Figure 7.18: Options for ticket assignment and routing
Customer Registration 粒度分析:
- 客戶註冊、個人資料管理和帳務功能是否應該拆分?
- 分析 disintegrators:這些功能內聚性較低、變動性不同、安全需求不同
- 分析 integrators:不需要同一個資料庫交易、服務間通訊有限
- 最終決定:拆分為獨立服務是合理的
