跳至主要內容
ESC
PCA 雲端架構師之旅 — 第 5/13 篇

PCA 雲端架構師之旅 05 — Microservices 拆分

PCA 雲端架構師之旅 05 — Microservices 拆分
Updated: 2026-07-17

「我們要微服務化」這句話,我聽過太多次了。然後一上雲就把單體(monolith)一刀剁成 20 個服務,每個都得跟另外 19 個講話,最後變成分散式單體(distributed monolith)——明明拆開了,卻比原本還難改、還難測、還難救。

這是微服務最大的陷阱:拆得動,不代表拆得對。PCA 考試很愛在這裡設局,題目會描述一堆現象,要你判斷邊界該畫在哪、服務之間怎麼溝通,而不是看你會不會一鍵開 GKE。

這是 PCA 雲端架構師之旅 的第五步。上一篇我們把 SLO / SLI 訂好了(04 · SLO / SLI),這一步就是把那些 SLO 跟前面的 user story,落成一張實際的服務地圖。

共用資料庫與交錯依賴的分散式單體,改成依商業能力切分、各自擁有資料並以事件溝通的微服務圖

圖解:把程式切成很多份,卻讓所有服務共用同一個資料庫、彼此到處同步呼叫,只是把單體變得更難救。好的邊界跟著商業能力與團隊責任切,每個服務掌握自己的資料,只在真的需要時透過簡單介面或事件交換資訊。


題目其實一直在偷偷告訴你邊界在哪

PCA 的 case study 不會直接寫「請拆成 N 個服務」,它會藏在敘述裡。你會看到這類句子:

  • 「某個團隊希望能獨立部署、不受其他團隊發版節奏影響」
  • 「促銷模組每週都在改,但訂單核心半年才動一次」
  • 「某個功能的尖峰流量是其他功能的 10 倍」

這每一句,都是在劃服務邊界。一個團隊想獨立部署,就是在說「這塊該是自己的服務」;變更速率差這麼多,就是在說「別綁在一起發版」;流量差 10 倍,就是在說「scaling 特性不同,分開才好擴」。讀題的時候把這些線索圈出來,答案幾乎自己就浮出來了。

最怕的是看到「拆」就反射性地全塞 GKE、全部拆到底。考試要的是合理的拆法,不是最多服務的拆法。

📝 考場提點

看到「獨立部署」「不同發版頻率」「流量差好幾倍」「這個模組碰信用卡 / 病歷」這幾種字眼,先在腦中標記「這裡有一條邊界」。考題給的選項通常會有一個「全部塞同一個服務」跟一個「拆到奈米級」的極端,這兩個多半是陷阱——正確答案常常落在中間那個「照業務能力分、數量適中」的選項。別被「微服務 = 越多越潮」帶偏。


拆之前,先搞懂你是照什麼軸在切

新人最常見的災難,是用錯的軸去拆。我看過兩種,都很慘:

一種是按技術層拆——資料層一個服務、邏輯層一個服務、前端一個服務。聽起來很整齊,但這根本是把三層式架構畫成三個盒子而已,每改一個小功能還是得三個一起動,等於沒拆。

另一種是按資料表拆——一張表配一個服務。結果下一筆訂單要連續呼叫十個服務,網路來回的延遲全疊上去,還隨便哪個掛了整單就死。

真正該看的是這三條軸:

  • 按業務能力(business capability)切。 一個服務對應一件業務上說得出口的事——「下單」「查庫存」「算促銷價」。標準很簡單:你能不能用一句業務的話介紹這個服務在幹嘛?講不出來,多半切錯了。
  • 按變更速率(rate of change)切。 天天在改的跟半年不動的,硬綁在一起就互相拖累——少動的那塊被迫跟著一直重新測試、重新部署。分開才能各走各的節奏。
  • 按擴展特性(scaling profile)切。 讀多寫少、計算密集、I/O 密集,這三種要的機器資源完全不同。混在一個服務裡,你連 autoscaling 都很難調。

這三條軸常常會打架——某塊照業務能力該合,照變更速率又該分。沒有標準答案,這時候就回去看你的 SLO 跟團隊現實,哪條軸對這個系統最痛就聽哪條的。


DDD 那幾個詞,講白話就好

聊微服務邊界一定會碰到領域驅動設計(Domain-Driven Design,DDD)。它的詞彙聽起來很學術,但意思其實很樸素:

Bounded Context(限界上下文),就是「一塊領域的地盤」。訂單是一塊、庫存是一塊、會員是一塊,各自有各自的規則跟語言——同樣叫「商品」,在 catalog 裡指的是可以被搜尋瀏覽的東西,在 inventory 裡指的是有幾件可賣的數字,這兩個其實是不同的東西。一個 bounded context 通常就對應一個微服務,這是最自然的起手式。

Aggregate(聚合),是「一份要綁在一起保證一致的資料」。一張訂單底下有品項、金額、狀態,這些得一起改、一起對——你不能訂單金額改了品項沒改。這一坨就是一個 aggregate,而 aggregate 不能跨服務切開,不然你就得自己手刻分散式交易,那是另一個地獄。

Context Map(上下文地圖),就是把各塊地盤之間的關係畫成一張圖:誰呼叫誰、誰訂閱誰的事件、哪兩塊其實該合併。畫出來你才看得到整體耦合長怎樣。

實務上一個小技巧:aggregate 的邊界往往就是你的服務邊界。先找出「哪些資料必須強一致」,把它們圈成一個 aggregate,服務邊界八九不離十就跟著出來了。


服務之間怎麼講話,比拆幾個還重要

拆完之後真正考驗架構功力的,是服務之間怎麼溝通。這塊選錯,前面拆得再漂亮都白搭——我看過拆得很乾淨的系統,因為全部用同步呼叫串起來,結果一個下游慢三秒、整條鏈全部 timeout,比單體還脆弱。

幾種主要的溝通方式,以及在 GCP 上對應的服務:

模式什麼時候用GCP 對應
同步 REST / gRPC即時要結果、強依賴Cloud Run + Internal ALB、GKE
非同步事件(Pub/Sub)可以解耦、容忍一點延遲Pub/Sub
工作佇列要重試、要限流Cloud Tasks
批次資料管線大量、非即時Dataflow、BigQuery

選的訣竅其實一句話:能不等就別等。使用者真的需要當下拿到結果(例如結帳前查庫存夠不夠),才用同步呼叫;只要是「做完通知一聲就好」的事(例如訂單成立後寄通知、更新搜尋索引),就丟事件,讓下游自己慢慢消化。

Pub/Sub 建立訂閱項目表單,訂閱 order-payment-requested 主題;傳送類型可選提取(Pull)、推送(Push)、寫入 BigQuery、寫入 Cloud Storage,確認期限設為 10 秒,下方列出重試政策、dead-letter 主題、僅傳送一次(exactly-once)與訊息排序等失敗處理和放送屬性選項

別被「丟事件,下游自己慢慢消化」騙了——「消化」背後的眉角全在這張訂閱設定裡。Pub/Sub 預設是「至少一次」送達,訊息可能重送,所以消費端一定要做成 idempotent(同一筆付款重複收到也不會重複扣款),這是 PCA/PDE 最愛埋的陷阱。確認期限(ack deadline)這裡設 10 秒,超時沒回 ack 就重送;dead-letter 則讓一直處理失敗的「毒藥訊息」別無限重試、卡死整條訂閱。至於「僅傳送一次」和「訊息排序」聽起來很香,但都會犧牲吞吐、還有同區域等限制,別反射性地全開。

過來人的血淚是:同步呼叫鏈越短越好。A 同步呼叫 B、B 再同步呼叫 C、C 又呼叫 D,這條鏈的總延遲是每一段相加,而且任何一段抖一下、整條都受影響。一個經驗法則是超過 3 跳就該停下來想:這幾段裡有沒有哪一段其實可以改成事件、不用卡在那裡等?


走一遍範例 — 登雲書店

光講原則太抽象,回到我們的老朋友登雲書店。後面幾步也會一直用到這份拆法,這裡先把它讀熟。

根據前面幾篇定下來的 persona、user story 跟 SLO,登雲書店切出 8 個核心服務,每個對應一個 bounded context:

#服務名職責變更速率SLO 重點
1catalog-service商品目錄、搜尋中(每週)讀多寫少、99.9%
2inventory-service庫存數量、預留低(每月)強一致、99.99%
3pricing-service定價、促銷高(每日)讀多寫少、99.9%
4cart-service購物車高 QPS、99.9%
5order-service訂單生命週期99.99%、Saga 協調
6payment-service金流整合PCI 隔離、99.99%
7ebook-service電子書閱讀進度寫多讀多、99.9%
8partner-ingest-service書商批次匯入吞吐導向、99%

為什麼是這樣切,每一刀都有理由:

catalog 跟 inventory 一定要分開。 這是最容易被新人合在一起的兩個——它們都叫「商品」嘛。但 catalog 被大量快取、流量超高、讀多寫少;inventory 要強一致才不會超賣,寫入路徑得謹慎。一個追求快取命中率、一個追求一致性,scaling 跟設計取向完全相反,綁一起兩邊都做不好。

pricing 獨立出來。 促銷規則三天兩頭在改,行銷常常今天談好一檔活動明天就要上。如果它跟訂單、庫存綁死,每次改個折扣都要連動部署一堆東西,沒人敢動。拆開來,pricing 自己每天發版都不關別人的事。

payment 不只獨立,還放在獨立的 GCP project。 這是 PCI DSS 的硬要求——卡號接觸的範圍要降到最小(minimized scope)。把金流關進自己的 project,就能精準控制「只有這個 project 碰得到卡號」,稽核範圍也跟著縮小,未來過 PCI 評估會省下大把力氣。這一刀切的不只是技術,是合規。

partner-ingest 獨立。 書商批次匯入是典型的「突然來一大坨、平常很閒」的工作型態,跟線上即時服務的資源曲線天差地遠。放在一起,一場大批次匯入就可能把線上服務的資源吃光。隔出來,互不干擾。

服務切好了,接著看它們怎麼互相溝通:

  • 同步呼叫用在使用者真的在等結果的地方:cart → pricing(顯示現價)、cart → inventory(結帳前確認還有貨)。
  • 非同步事件用在不該卡住主流程的地方。order → payment 走 Pub/Sub:order 把付款請求丟出去就先去處理別的,不會傻等銀行 API 回那 20 秒;payment 處理完再發事件通知 order 更新狀態。
  • 事件廣播用在「一件事發生、好幾個人都想知道」的場景:inventory 一變動就發 inventory.updated,catalog 跟搜尋索引各自訂閱、各自更新,彼此不用認識對方。
  • 批次管線給 partner-ingest 用:透過 Dataflow 把書商資料寫進 inventory 與 pricing 的專屬 import table,校驗無誤後再交換進正式表,避免髒資料直接污染線上。

最後是一些跨服務的共同規矩,這在大團隊很關鍵:

  • 每個服務一個 GCP project(或至少一個 namespace),用 Workload Identity Federation for GKE(Google 2024 年把原本的 Workload Identity 併入這個名稱)管 IAM,服務身分清清楚楚。
  • 服務間呼叫走 Internal Application Load Balancer(內部 ALB,舊稱 Internal HTTP(S) LB)或 gRPC,不暴露到公網;對外統一只開 API Gateway 一個門。
  • 共用的資料格式放在同一個 repo 的 schemas/ 目錄,用 Protobuf 加 Buf 做版本控管,避免某個服務偷改欄位把下游全打掛。

每做一個拆分決定,回頭對一次題目給的需求:這刀是真的對應到某個業務能力 / SLO / 合規要求,還是只是我覺得拆開比較整齊?後者就是過度設計。


拆過頭,和拆不夠,都會出事

拆得對不對,有幾條界線你心裡要有數:

別拆成奈米服務。 8 個對登雲書店剛好,30 個就是自找麻煩——服務一多,光是服務之間的呼叫、監控、部署協調就能把團隊淹死。我的建議向來是寧可先粗一點,從 3 到 5 個起步,跑一陣子看哪裡真的痛、真的該獨立了,再拆。拆開永遠比合併容易。

兩個服務共用同一個資料庫,等於沒拆。 這是最隱蔽的假微服務:表面上兩個服務,底層共一張表,改個 schema 兩邊一起爆。每個服務必須擁有自己的儲存,這條沒有商量餘地——資料的 owner 只能有一個。

同步鏈別超過 3 跳。 前面講過,A → B → C → D 的同步串接,一個慢全部慢。超過 3 跳就回頭看看哪段能事件化。

還有一條最容易被工程師忽略的——Conway’s Law。 這條定律白話講就是:系統最後會長得像你的組織。一個 6 人的維運團隊,你硬切 8 個服務,平均一人顧一個還有剩,半夜誰的服務出事、誰來救?沒人。拆分數量得跟團隊能扛的量級對齊,這不是技術問題,是現實問題。考試裡只要 case study 提到團隊規模很小,「先別拆太細」往往就是更穩的答案。

📝 考場提點

「拆太細」在考場是高頻陷阱。題目給你一個小團隊、卻有個選項把系統切成十幾個微服務,那個選項通常就是來釣你的——再加上 Conway’s Law 一想就知道顧不過來。同樣地,看到「共用資料庫」「同一張表多服務寫入」的選項,幾乎都是錯的,因為它違反「一份資料一個 owner」。時間上,這類題不必糾結到完美邊界,抓住「按業務能力拆 + 數量適中 + 各服務獨享儲存」三個原則就能穩穩拿分,省下的時間留給後面的計算題。


延伸閱讀


下一步:服務邊界有了,接著就要設計它們對外講話的契約,也就是 REST API 設計

🎯 換你練習

理論讀完,換自己來。到 架構師設計工作坊 · 步驟 5 填入你的 case study,邊寫邊內化。

PCA 雲端架構師之旅 — 5/13 完成 查看系列全覽 →

留言討論

徽章解鎖!