PCA 雲端架構師之旅 05 — Microservices 拆分
「要不要拆成微服務?」先別只看程式有多大。如果拆開後每個服務仍直接改同一批資料表、每次都得一起部署,獨立服務的好處就很有限,還多了網路呼叫要維護。
這篇會用登雲書店的例子,看團隊責任、變更頻率與資料歸屬能否形成清楚邊界,再決定哪些部分值得獨立部署。
這是 PCA 雲端架構師之旅 的第五步。上一篇我們把 SLO / SLI 訂好了(04 · SLO / SLI),這一步就是把那些 SLO 跟前面的 user story,落成一張實際的服務地圖。

圖解:把程式切成很多份,卻讓各服務直接修改同一批表、彼此到處同步呼叫,只會增加協調成本。邊界應依業務責任與資料擁有權規劃,服務間再透過清楚的介面或事件交換資訊。
從需求找服務邊界
案例通常不會直接指定要拆幾個服務,要自己從需求裡找出哪些部分不好綁在一起。這類線索像是:
- 「某個團隊希望能獨立部署、不受其他團隊發版節奏影響」
- 「促銷模組每週都在改,但訂單核心半年才動一次」
- 「某個功能的尖峰流量是其他功能的 10 倍」
這些都是拆分的線索,還不是結論。促銷與訂單的發版頻率不同,值得看看能否各自部署;流量差很多,則要確認擴縮是否互相拖累。但如果兩邊仍須共用大量資料、每次變更都要協調,拆成兩個服務未必比較輕鬆。
服務邊界也不等於部署平台。先決定哪些責任要分開,再評估團隊能否維運,以及適合用哪種運算服務承接。
📝 考場提點
讀到獨立部署、不同發版頻率或資料隔離要求時,先標記可能的邊界,再問三件事:資料由誰負責?能否獨立測試與上線?拆開後的跨服務呼叫會增加多少?不要只靠服務數量判斷架構好壞。
流程圖暫時無法顯示。可先查看下方的流程與節點文字,或重新整理頁面後再試。
查看流程與節點文字
箭頭表示流程方向;方括號與引號內是節點名稱,可搭配下方圖解閱讀。
flowchart TB
C[一組功能] --> B{屬於不同業務能力?}
B -- 是 --> S[傾向拆分]
B -- 否 --> R{變更頻率或擴展曲線<br/>明顯不同?}
R -- 是 --> S
R -- 否 --> G{合規或資料隔離要求不同?}
G -- 是 --> S
G -- 否 --> K[先保持同一服務]
S --> T{團隊能獨立維運?}
T -- 否 --> K
T -- 是 --> D[建立清楚邊界與 owner] 拆之前,先搞懂你是照什麼軸在切
拆服務時,有兩種看似整齊的切法需要特別小心:
一種是按技術層拆:資料層一個服務、邏輯層一個服務、前端一個服務。如果每改一個功能都得讓三個團隊同步發版,邊界只是多了網路呼叫,沒有換來獨立性。
另一種是按資料表拆:一張表配一個服務。下單時若得接連呼叫許多服務,延遲與故障點都會增加。資料表數量不等於業務邊界數量。
比較有用的是這三條軸:
- 按業務能力(business capability)切。 例如「下單」「查庫存」「算促銷價」。如果很難說清楚某個服務負責哪段業務流程,它的責任可能還沒劃清。
- 按變更速率(rate of change)切。 天天在改的功能若與半年才動一次的核心流程綁在一起,後者可能被迫跟著頻繁測試和部署。
- 按擴展特性(scaling profile)切。 讀多寫少、計算密集和 I/O 密集的工作負載,資源需求不同;分開後才可能各自調整容量。
這三條軸不一定指向同一個切法。遇到衝突時,回頭看 SLO、資料一致性和團隊的值班能力:拆開帶來的好處,是否足以抵過新增的協調成本?
流程圖暫時無法顯示。可先查看下方的流程與節點文字,或重新整理頁面後再試。
查看流程與節點文字
箭頭表示流程方向;方括號與引號內是節點名稱,可搭配下方圖解閱讀。
flowchart TB
A[候選邊界] --> D[先圈出必須強一致的 Aggregate]
D --> B[再看業務能力與資料 owner]
B --> C[比較變更速率與擴展特性]
C --> S[核對每個服務的 SLO]
S --> O[核對團隊 ownership<br/>與 on-call 能力]
O --> V{拆分收益大於<br/>分散式複雜度?}
V -- 否 --> M[維持較粗服務]
V -- 是 --> X[確認獨立部署與儲存] DDD 的幾個詞,放進書店例子裡看
領域驅動設計(Domain-Driven Design,DDD)提供了一套討論邊界的語言。先不用急著把每個名詞對到一個服務,看看它們各自回答什麼問題:
Bounded Context(限界上下文)說的是一套概念在哪個範圍內有效。同樣叫「商品」,目錄關心名稱與展示,庫存關心可賣數量。把兩邊的規則分清楚,有助於劃界;但一個 bounded context 不一定要立刻變成一個獨立服務。
Aggregate(聚合)用來界定哪些資料需要在同一筆交易裡維持規則。例如訂單金額與品項若必須同步更新,就要有明確的交易邊界。若把這個邊界拆到多個服務,還得另外設計跨服務的一致性處理。
Context Map(上下文地圖)則把各個上下文的關係畫出來:誰提供資料、誰呼叫誰、誰訂閱事件。畫出來後,比較容易看出哪些依賴會妨礙獨立部署。
所以可以先圈出必須一起維持一致的資料,再看團隊責任和部署需求。Aggregate 是設計服務邊界的重要線索,不是「一個 aggregate 一個服務」的公式。
服務之間怎麼講話,比拆幾個還重要
拆出服務後,還要決定它們怎麼溝通。如果每一步都同步等待下游回應,一個服務變慢就可能拖住整條請求鏈;需要即時結果的呼叫與可以稍後處理的事件,應分開設計。
幾種主要的溝通方式,以及在 GCP 上對應的服務:
| 模式 | 什麼時候用 | GCP 對應 |
|---|---|---|
| 同步 REST / gRPC | 即時要結果、強依賴 | Cloud Run + Internal ALB、GKE |
| 非同步事件(Pub/Sub) | 可以解耦、容忍一點延遲 | Pub/Sub |
| 工作佇列 | 要重試、要限流 | Cloud Tasks |
| 批次資料管線 | 大量、非即時 | Dataflow、BigQuery |
表格超出欄寬時,可左右捲動;鍵盤可聚焦表格後用左右方向鍵查看。
如果使用者當下就需要結果,例如結帳前確認庫存,同步呼叫比較直觀。寄通知或更新搜尋索引可以在訂單成立後再處理,但改成事件後還要顧重試、重複投遞和失敗追蹤,不能只把訊息送出去就算完成。

事件送出後,仍要替消費端設計失敗處理。Pub/Sub 可能重送訊息,付款等有副作用的處理應能辨識重複事件。確認期限(ack deadline)設為 10 秒時,超時未確認的訊息可能再次派送;dead-letter topic 則可讓反覆失敗的訊息離開一般處理路徑。「僅傳送一次」與訊息排序也各有適用條件,應按需求設定。
同步呼叫串得越長,使用者等待的時間和受下游故障影響的機會通常都會增加。若 A 等 B、B 等 C、C 又等 D,就檢查其中哪一步真的必須在回應前完成;能延後的工作,再考慮改成事件。三跳不是硬上限,重點是每一跳都要有理由。
流程圖暫時無法顯示。可先查看下方的流程與節點文字,或重新整理頁面後再試。
查看流程與節點文字
箭頭表示流程方向;方括號與引號內是節點名稱,可搭配下方圖解閱讀。
flowchart LR
I[服務需要另一個結果] --> W{使用者正在等待<br/>且沒有結果就不能繼續?}
W -- 是 --> S[同步 REST/gRPC]
W -- 否 --> O{指定單一工作<br/>需要限流與重試?}
O -- 是 --> T[Cloud Tasks 工作佇列]
O -- 否 --> F{多個下游都要知道?}
F -- 是 --> P[Pub/Sub 事件廣播]
F -- 否 --> B[批次或資料管線]
S --> L{同步鏈過長或<br/>下游容易延遲?}
L -- 是 --> V[檢查可延後的工作<br/>與逾時策略]
L -- 否 --> E[保留短同步路徑] 走一遍範例 — 登雲書店
回到登雲書店,把前面的使用者需求、故事和 SLO 放進同一張表。後面幾步會沿用這些責任範圍。
下表先列出 8 個候選模組。它們有不同的資料與變更需求,但不表示第一天就要部署成 8 個獨立服務;要不要拆,仍需看團隊是否能各自維運。
| # | 候選模組/服務 | 職責 | 變更速率 | SLO 重點 |
|---|---|---|---|---|
| 1 | catalog-service | 商品目錄、搜尋 | 中(每週) | 讀多寫少、99.9% |
| 2 | inventory-service | 庫存數量、預留 | 低(每月) | 強一致、99.99% |
| 3 | pricing-service | 定價、促銷 | 高(每日) | 讀多寫少、99.9% |
| 4 | cart-service | 購物車 | 中 | 高 QPS、99.9% |
| 5 | order-service | 訂單生命週期 | 低 | 99.99%、Saga 協調 |
| 6 | payment-service | 金流整合 | 低 | PCI 隔離、99.99% |
| 7 | ebook-service | 電子書閱讀進度 | 中 | 寫多讀多、99.9% |
| 8 | partner-ingest-service | 書商批次匯入 | 低 | 吞吐導向、99% |
表格超出欄寬時,可左右捲動;鍵盤可聚焦表格後用左右方向鍵查看。
可以逐一檢查哪些邊界值得保留:
catalog 與 inventory 的需求不同。 商品目錄通常讀多寫少,適合快取;庫存則要留意寫入競爭和超賣。在這個案例裡把兩者分開,能讓它們各自調整擴充與資料一致性策略;規模較小時,也可以先保留清楚的模組邊界,不急著拆成兩個服務。
pricing 是否要獨立? 若促銷規則常改、每次都得跟訂單服務一起發版,獨立部署可能有價值。但訂單建立時仍要保存成交價格與規則版本,不能因為服務拆開就讓價格在結帳途中變動。
payment 需要清楚的安全邊界。 案例把金流服務放在獨立 project,方便限制權限、整理稽核紀錄與盤點資料流;但是否符合 PCI DSS,還得看整體系統與實際處理的資料,不能只靠專案切分判定。
partner-ingest 與線上請求分開。 書商批次匯入有集中處理、失敗重試的需求;若和線上服務共用同一組資源,尖峰時可能互相影響。可以先隔離工作佇列與運算容量,是否要拆成獨立服務,再看部署與維運成本。
服務切好了,接著看它們怎麼互相溝通:
- 同步呼叫用在下一步必須等結果時,例如結帳前確認價格與庫存;要設定逾時,並處理下游失敗。
- 非同步處理適合可稍後完成的步驟。若付款也改成非同步,訂單就必須保留「待付款」狀態,不能在確認成功前告訴使用者已付款。
- 事件廣播適合讓多個下游各自更新,例如庫存變更後,商品頁與搜尋索引分別處理;消費端仍要能重試與去重。
- 批次匯入先驗證書商資料,再交給 inventory 與 pricing 各自負責的寫入流程。每日 200MB 的 CSV 不必預設使用 Dataflow,先看轉換複雜度和處理期限。

跨服務流程不能假裝成一筆資料庫交易。每個服務只提交自己的本地狀態,再用事件推進;付款失敗時則發布明確事件,依序釋放庫存並取消訂單。事件可能重送,因此每個消費端仍需用冪等鍵去重。
服務之間還需要約定幾件事:
- 身分與權限:讓工作負載使用自己的身分。若服務跑在 GKE,可評估 Workload Identity Federation for GKE;project 和 namespace 邊界則依團隊與隔離需求規劃。
- 連線路徑:確認哪些 API 只允許內部呼叫、哪些需要公開入口。內部負載平衡器是入口選項,gRPC 是通訊協定,兩者不是二選一。
- 介面契約:請求與事件格式要有版本管理和相容性檢查;是否採 Protobuf、Buf 或共用 schema repo,取決於團隊使用的協定與部署流程。
每做一個拆分決定,回頭對一次題目給的需求:這刀是真的對應到某個業務能力 / SLO / 合規要求,還是只是我覺得拆開比較整齊?後者就是過度設計。
拆過頭,和拆不夠,都會出事
拆得對不對,有幾條界線你心裡要有數:
別先決定要拆幾個。 服務越多,監控、部署和跨服務協調的工作也越多。先保留能清楚負責的模組邊界;某一部分真的需要獨立部署、擴展或管控時,再評估拆出來。沒有適用所有團隊的服務數量。
資料擁有權不清楚。 若兩個服務都能直接改同一張表,schema 變更就容易互相牽動。資料應有明確 owner;初期即使共用資料庫實例,也可先分開 schema 與寫入權限,不必為了形式立刻建立多套儲存。
同步鏈越長,越要檢查逾時與故障傳遞。 A → B → C → D 的每一段都在等待下游;若整條鏈對延遲很敏感,就檢查哪些步驟可以改成非同步處理。
團隊能不能顧得來? Conway’s Law 提醒我們,系統邊界常會反映團隊的溝通方式。若只有少數人值班,拆出更多服務前,先確認誰負責告警、部署與事故處理;否則獨立部署的好處可能被維運負擔抵消。
📝 考場提點
小團隊若要維運十幾個微服務,先問誰負責部署、監控和事故處理。多個服務若同時直接寫一張表,也要檢查資料擁有權是否清楚。題目給的團隊規模、變更頻率與資料邊界,比「服務數量越多越好」更有判斷價值。
流程圖暫時無法顯示。可先查看下方的流程與節點文字,或重新整理頁面後再試。
查看流程與節點文字
箭頭表示流程方向;方括號與引號內是節點名稱,可搭配下方圖解閱讀。
flowchart TB
S[檢查一個服務] --> O{有單一業務職責<br/>與明確 owner?}
O -- 否 --> R[重新合併或劃界]
O -- 是 --> D{資料 owner<br/>與寫入權限清楚?}
D -- 否 --> R
D -- 是 --> P{能獨立部署與擴展?}
P -- 否 --> R
P -- 是 --> C{同步依賴是否短且必要?}
C -- 否 --> E[檢查逾時、重試<br/>與非同步可能性]
C -- 是 --> H[邊界暫時健康]
E --> H 延伸閱讀
- Microservices.io — Decomposition Patterns — Chris Richardson 的經典拆分方法。
- GCP — What Is Microservices Architecture? — 官方微服務架構概念說明。
- DDD Reference by Eric Evans — DDD 用語官方 PDF。
- SRE Workbook — Non-Abstract Large System Design — 系統分解的 SRE 觀點。
下一步:服務邊界有了,接著就要設計它們對外講話的契約,也就是 REST API 設計。
🎯 換你練習
理論讀完,換自己來。到 架構師設計工作坊 · 步驟 5 填入你的 case study,邊寫邊內化。
讀完後,留一個學習紀錄
試著用自己的話說出一個重點,以及你會如何運用。實作文章請先核對結果與清理資源,再標記為讀完。
文章進度以你確認讀完的標記為準,可隨時取消。舊版造訪紀錄保留,但不計入讀完;這份自評不代表已掌握內容。