Service Granularity#

決定服務的適當粒度(granularity)– 即服務的大小 – 是軟體架構中最困難的部分之一。架構師和開發團隊經常為此掙扎。

ModularityGranularity 是不同的概念:

  • Modularity(模組化):關注將系統拆分為獨立部分
  • Granularity(粒度):關注這些獨立部分的大小

分散式系統中的大多數問題和挑戰通常與粒度有關,而非模組化。

衡量服務粒度的方式:

  • 服務的粒度不由類別數量或程式碼行數定義,而是由服務做什麼來決定
  • 一個客觀的衡量指標是計算服務中的 statements 數量
  • 另一個指標是衡量服務暴露的 public interfacesoperations 數量

兩股相反的力量決定了服務粒度: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 之間找到平衡
  • 沒有統一的正確答案,取決於具體的業務需求和技術約束
  • 建議的做法:
    1. 列出所有適用的 disintegrators(拆分的理由)
    2. 列出所有適用的 integrators(合併的理由)
    3. 客觀地權衡兩側的力量
    4. 做出決定並記錄在 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:不需要同一個資料庫交易、服務間通訊有限
  • 最終決定:拆分為獨立服務是合理的

Figure 7.19: Options for customer registration