📌 30 秒摘要(Layer 3)

微服務的權威實作指南。它不只講「怎麼拆」,更講「該不該拆、拆的代價」。核心洞見兩條:服務邊界要對齊業務領域(用 DDD 的限界上下文找切點);組織結構會決定系統結構(康威定律)——所以拆服務也是在拆團隊。其餘的分散式取捨(容錯、事務、部署)與 鳳凰架構 共通,見既有工具。

🗺 心智圖(Canvas)

微服務設計

微服務設計

Sam Newman|該不該拆、拆的代價

🧭 邊界對齊業務領域 高內聚低耦合

🎯 什麼情境該想到我

當系統/模型越長越大越糊,或發現「同一個名詞在不同部門意義不同」時。

⚙️ 怎麼用

  1. 劃出邊界:在某個邊界內,一套模型與 工具-通用語言 保持一致、無歧義。
  2. 一個詞在不同上下文意義不同 → 就該分成不同上下文(例:「商品」在銷售 vs 物流意義不同)。
  3. 用 Context Map 描述上下文之間的關係(上下游、防腐層 ACL、共享核心…)。
  4. 邊界也是團隊、部署(含微服務拆分)的自然切點。

★ 核心洞察:同一個詞,在不同上下文是不同的東西

「訂單 Order」
  銷售上下文:客戶、商品、金額、折扣          ← 關心「賣了什麼」
  倉儲上下文:SKU、數量、重量、儲位            ← 關心「拿什麼出貨」
  財務上下文:應收金額、稅、發票號碼、帳期      ← 關心「錢怎麼算」

❌ 建一個「統一的 Order 模型」滿足三方
   → ★ 30 個欄位,每一方只用 10 個,但每次改動都要三方一起 review
   → 這就是「統一模型」必然的下場

★ 限界上下文的主張是反直覺的:不要追求統一模型,而是承認「同一個名詞有多個模型」,並明確劃出邊界。
邊界內:術語一致、模型統一(這就是 工具-通用語言)。
邊界外:透過明確的轉換來往,不共用模型。

★ 怎麼找出邊界(四個實用訊號)

訊號說明
★ 語言的斷裂同一個詞在兩群人口中意思不同 → 那裡就是邊界
★ 一起改變的東西用 git log 看哪些檔案總是一起被改(見 工具-影響分析)
★ 組織結構不同團隊負責的部分天然會形成邊界(工具-康威定律)
交易一致性的需求★ 必須在同一個交易裡的東西,不該跨上下文

★ 最後一條特別重要:如果你發現「切開之後需要分散式事務」,
那八成是切錯了(見 工具-分散式事務的取捨)。

★ 上下文之間的七種關係(上下文對應 Context Map)

關係意思何時
★ 防腐層 ACL我方寫一層翻譯,把對方的模型轉成我的★ 對方模型很糟或會變時的標準做法
開放主機服務 OHS我提供一個公開的、穩定的協定給所有下游下游很多時
發布語言 PL用一個大家同意的中介格式(Protobuf schema、事件定義)配合 OHS
客戶-供應商下游能影響上游的排程同公司內
追隨者下游完全接受上游的模型,不做轉換★ 上游穩定且模型合用時(省事,但耦合)
共享核心兩個上下文共用一小塊模型與程式碼⚠️ 要非常小心,見下
各行其道完全不整合沒有整合需求時

★ 共享核心是最危險的一個:

共用的那一塊同時屬於兩個團隊,改它要兩邊同意。
它會慢慢長大,最後變成一個誰都不敢改的巨石。
→ 預設用防腐層;只在「共用的部分極小且極穩定」時才考慮共享核心。

★ 邊界 ≠ 微服務

限界上下文  = ★ 邏輯邊界(模型與語言的邊界)
微服務      = ★ 部署邊界(可獨立部署的單元)

★ 一個限界上下文「可以」是一個微服務,但不必是。
在單體裡用模組/套件劃出限界上下文,是完全正確且通常更務實的做法
——你拿到了模型的清晰,卻不用付分散式系統的代價。

★ 先在單體內劃清邊界,等邊界被驗證穩定了,再考慮拆成服務(見 工具-服務邊界與演進式拆分)。

★ 實際長什麼樣(Go)

internal/
  sales/          ← ★ 限界上下文
    order.go        type Order struct { CustomerID, Items, Discount ... }
  warehouse/      ← ★ 另一個限界上下文
    order.go        type Order struct { SKUs, Weight, Location ... }   ★ 同名不同型別,這是對的
    acl.go          ★ 防腐層:把 sales 的事件翻譯成 warehouse 的模型
  billing/
    invoice.go

★ 關鍵:warehouse 不 import sales 的型別,只透過事件或 ACL 取得它需要的資訊。
這可以用架構測試自動驗證(見 工具-好萊塢原則與依賴反轉)。

🧪 我實際套用的紀錄

  • 2026-07-14:(待填)

⚠️ 注意 / 什麼時候不適用

  • ★ 小系統不需要。整個團隊五個人、一個產品、術語一致 → 一個上下文就好。
    限界上下文解決的是「不同群人對同一個詞有不同理解」的問題——沒有這個問題就不需要它。
  • ★ 一開始就切邊界通常會切錯。你還不夠了解領域。
    → 先寫成一個模組化的單體,讓邊界在實作中浮現(見 工具-服務架構的演進與拆分)。
  • ★ 「重複」是刻意的,不是疏失。兩個上下文各有一個 Order,這不是違反 DRY——
    它們是不同的概念碰巧同名(見 工具-DRY原則 對「知識的重複 vs 程式碼的重複」的區分)。
    硬要合併才是錯的。
  • 邊界要有人守。沒有 review 紀律或架構測試,import 會慢慢穿透邊界,最後回到泥球。
  • 上下文對應要畫出來並維護。它是團隊之間的契約,不是一張畫完就丟的圖。
  • 不要為了「以後好拆微服務」而過度切割。切太細會讓每個改動都要跨好幾個上下文。

🔗 相關工具

🎯 什麼情境該想到我

當你要決定「一個微服務該多大、邊界畫在哪、從單體怎麼拆」時。

⚙️ 怎麼用

  1. 按業務能力/限界上下文切(見 工具-限界上下文),不按技術分層切。
  2. 檢驗好邊界:服務能獨立變更、獨立部署嗎?若一個改動總要同時改好幾個服務 → 邊界錯了。
  3. 每個服務擁有自己的資料:不共用資料庫,透過 API/事件互動(避免隱性耦合)。
  4. 服務間別太聊天:若兩服務呼叫極頻繁,可能該合併——高內聚該在同一服務內。
  5. 演進式拆分:從單體開始,沿著真實痛點與清楚的接縫逐步剝離(見 工具-架構隨業務規模演進)。

📌 這一組三張卡:工具-服務架構的演進與拆分=「有哪些選項」|工具-架構隨業務規模演進=「什麼時候動」|本卡=「切在哪、怎麼切」

★ 檢驗邊界對不對的五個問題

① ★ 能獨立部署嗎?
   —— 每次發布都要和別的服務按順序一起上 → ★ 分散式單體,邊界錯了

② ★ 一個典型的功能改動,會動到幾個服務?
   —— 常常要改 3 個以上 → 邊界橫切了業務能力

③ ★ 服務之間的呼叫頻率高嗎?
   —— 一個請求要來回呼叫 5 次 → ★ 高內聚的東西被切開了,該合併

④ ★ 需要跨服務的交易嗎?
   —— 需要 → 聚合被切開了(見 [[工具-分散式事務的取捨]])

⑤ ★ 資料是各自擁有的嗎?
   —— 共用同一張表 → 沒有真的拆開,只是兩個程式連同一個 DB

★ 第 ① 題是最實用的單一判準:「能不能只部署這一個?」

★ 絞殺者模式(Strangler Fig):怎麼安全地剝離

① 在單體前面放一層「門面」(proxy / API gateway / 路由層)
     Client ──▶ [Facade] ──▶ 單體

② 建新服務,門面把「一小部分流量」導過去(先唯讀、先內部使用者)
     Client ──▶ [Facade] ─┬─▶ 單體
                          └─▶ 新服務   ← ★ 可隨時切回

③ ★ 雙寫 + 對帳:兩邊都寫,比對結果,確認新服務行為一致

④ 逐步提高流量比例,觀察指標

⑤ 舊路徑流量歸零後,★ 觀察一段時間再刪除

★ 關鍵是「每一步都可以立刻回退」。
大爆炸式重寫(big bang rewrite)失敗率極高,因為你在一個無法驗證的狀態下走了幾個月。

★ 最難的部分:拆資料

❌ 兩個服務共用同一個資料庫 → ★ 這不是微服務,是「兩個程式共用一個全域變數」
   任一方改 schema 都會弄壞對方,而且沒有編譯器會警告你

拆資料的步驟:

① 先拆「程式碼層」:所有存取都走一個 repository 介面(見 [[工具-接縫]])
② 拆「schema 層」:不同的表分開、拿掉跨表的外鍵與 JOIN
③ ★ 拆「實體層」:真正搬到不同的資料庫
④ 需要對方的資料時 → API 呼叫、事件複製、或 CQRS 讀模型

★ 步驟 ② 通常最痛:JOIN 不見了,要在應用層組合資料。
這也是為什麼「需要頻繁 JOIN 的資料不該被切開」——它是高內聚的訊號。

★ 切錯的三種典型形態

錯誤切法症狀正解
★ 按技術分層切api-service / logic-service / data-service
→ 每個功能都要改三個
按業務能力切
★ 切得太細(奈米服務)20 個服務,每個 200 行;一個請求跨 8 個服務合併。服務的大小該由「團隊能負責的範圍」決定
★ 切在聚合中間需要分散式事務才能維持不變式聚合不可切開(見 工具-實體值物件與聚合)

★ 服務之間該怎麼通訊

方式何時
同步 HTTP/gRPC★ 需要立即的回應、且呼叫鏈短
★ 非同步事件預設優先考慮:解耦、可重放、天然支援最終一致
共用資料庫❌ 不要
檔案/批次大量資料的定期同步

★ 同步呼叫會傳遞失敗:A → B → C,C 掛了 A 也掛。
每一層同步呼叫都在降低整體可用性(可用性相乘)。
→ 能非同步就非同步(見 工具-訊息傳遞整合模式)。

★ 拆之前先問:能不能不拆

合併回單體也是一個正當的架構決定。
如果兩個服務總是一起改、一起部署、互相頻繁呼叫——它們本來就是一個東西。
★ 「拆了之後發現不對就合併回去」不是失敗,是正確的演進。

🧪 我實際套用的紀錄

  • 2026-07-15:(待填)

⚠️ 注意 / 什麼時候不適用

  • ★★ 不要大爆炸重寫。用絞殺者模式,每一步可回退。
  • ★ 共用資料庫 = 沒有拆開。這是最常見的假微服務。
  • ★ 拆分期間會同時維護兩套,成本更高。要有明確的完成期限與「刪除舊路徑」的任務,
    否則會永遠停在中間狀態——那是最糟的狀態。
  • 雙寫期間的一致性:兩邊寫入可能不一致 → 一定要有對帳(見 工具-分散式事務的取捨)。
  • ★ 邊界會改變。業務變了,昨天對的邊界今天就錯了。保持能再切一次的能力,
    而不是追求「一次切對」。
  • 拆分的收益要能說清楚。「為了微服務」不是收益;「讓 A 團隊不用等 B 團隊部署」才是。
  • 先在單體內劃清邊界(模組化單體)再拆——這樣切錯的成本只是改資料夾。

🔗 相關工具

👥 組織決定架構 康威定律

🎯 什麼情境該想到我

當你發現「系統架構總是長得像組織圖」、或跨團隊協作成本高到架構改不動時。

⚙️ 怎麼用

「設計系統的組織,其產出的架構會複製該組織的溝通結構。」

  1. 想要某種架構,先設計對應的團隊結構(逆康威):想要獨立的微服務,就要有能獨立負責的小團隊。
  2. 一個服務由一個團隊擁有:邊界對齊團隊,減少跨團隊的協調摩擦。
  3. 團隊之間的介面 = 服務之間的介面:把高頻溝通留在團隊內,團隊間走清楚的契約。
  4. 組織重組時,預期架構也會(該)跟著變。

★ 原始陳述與它真正的含意

“Organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations.”
—— Melvin Conway, 1967

白話:系統的架構,會長成你的組織溝通結構的樣子。

為什麼必然如此:

兩個模組要能協作 → 兩群人要能溝通
溝通成本高的兩群人 → ★ 會傾向把介面定義得「簡單、少變動」
溝通成本低的一群人 → ★ 會傾向讓模組之間緊密耦合(因為改起來很方便)

★ 這不是「應該」的規範,是「已經發生」的觀察。
你的架構圖畫什麼不重要;真正決定架構的是「誰跟誰能輕鬆講上話」。

★ 逆康威手法(Inverse Conway Maneuver)

既然組織結構會決定架構,那就「先改組織,讓它長成你要的架構」。

想要:三個獨立部署、鬆耦合的服務
❌ 做法:一個大團隊分工寫三個服務
   → ★ 因為他們天天在一起,介面會越切越細、越耦合,最後還是一坨
✅ 做法:先分成三個「有完整能力、能獨立出貨」的團隊
   → 跨團隊溝通有成本 → 自然會把介面定得粗、穩定、少變動

★ 這是組織設計成為技術決策的少數場合之一,而且它的效果遠大於任何架構規範文件。

★ 「完整能力團隊」的判準

❌ 依技能分(水平切)✅ 依價值流分(垂直切)
前端組、後端組、DBA 組、QA 組★ 每個團隊有前端、後端、資料、測試
★ 一個功能要跨四個組排程★ 一個團隊能從需求到上線自己走完
交接多、等待多交接少
★ 沒人對「結果」負責團隊對結果負責

★ 判準:這個團隊能不能「自己把一個功能送到使用者手上」?
不能 → 它會產生大量的跨團隊協調成本,而那個成本最終會以「耦合的架構」的形式固化下來。

★ 團隊拓撲(Team Topologies)的四種團隊

類型做什麼
★ 串流對齊團隊(stream-aligned)對某條價值流負責,端到端。大多數團隊都該是這種
賦能團隊(enabling)幫其他團隊提升能力,不代做
複雜子系統團隊需要專業知識的部分(推薦演算法、風控引擎)
★ 平台團隊提供自助服務的內部平台,降低其他團隊的認知負荷

★ 平台團隊的關鍵是「自助」:

如果其他團隊要「開單請平台團隊幫忙」,那它不是平台,是瓶頸
——而且這個瓶頸會在架構上表現為「所有服務都依賴某個中心元件」
(見 自助服務平台)。

★ 認知負荷才是團隊規模的真正限制

一個團隊能負責多少東西,取決於它的認知負荷上限,不是人數。

團隊負責的服務越多、技術棧越雜、領域越廣
  → ★ 認知負荷超載 → 品質下降、交付變慢、人員流失

★ 這解釋了「兩個披薩團隊」為什麼有效——不是因為 6–8 人這個數字神奇,
而是因為超過這個規模,溝通路徑()與認知負荷就開始失控
(見 兩個披薩團隊、工具-布魯克斯定律)。

降低認知負荷的手段:縮小團隊負責的領域、提供好的平台與工具、減少團隊需要知道的「別人的細節」(這正是好的邊界在做的事)。

🧪 我實際套用的紀錄

  • 2026-07-15:(待填)

⚠️ 注意 / 什麼時候不適用

  • ★ 逆康威手法的代價很高。改組織會影響人的職涯、歸屬感與士氣。
    不要為了一個架構偏好就重組——要有清楚的業務理由。
  • ★ 改組織不會自動改好架構。既有的程式碼不會因為組織變了就重構——
    它只是讓「往正確方向演進」變得比較容易。 還是要有人去做那個重構。
  • 康威定律是描述性的,不是規範性的。它告訴你「會發生什麼」,不告訴你「該怎麼組織」。
  • ★ 地理與時區也是溝通結構。跨時區的團隊之間,介面自然會變粗——
    這可能是好事(強制解耦),也可能是災難(需要頻繁協調的部分被硬切開)。
  • 遠端工作改變了「溝通成本」的結構,所以也改變了康威定律的作用方式。不要照搬 1967 年的假設。
  • 小團隊不適用。十個人的公司,康威定律說的是「你們會產生一個單體」——而那是對的。

🔗 相關工具

🌱 演進式拆分 先單體,痛了再拆

🎯 什麼情境該想到我

當你在猶豫「要不要從單體走向微服務、怎麼拆」,或被「微服務很潮」帶風向時。

⚙️ 怎麼用

  1. 認清各階段的取捨:單體(簡單、部署一體)→ SOA → 微服務(獨立部署/擴展,但引入分散式複雜度)→ 無伺服器。微服務不是免費的——你用開發簡單度換來運維與分散式的複雜度。
  2. 先具備前置條件再拆:自動化部署、監控、服務治理都到位,才適合微服務。
  3. 按業務邊界拆,而非技術分層——切點對齊 工具-限界上下文。
  4. 能單體就先單體(modular monolith),真的遇到擴展/團隊瓶頸再拆。

📌 這一組有三張卡,分工如下:
本卡=「有哪些選項、各自的代價」|工具-架構隨業務規模演進=「什麼時候該演進」|工具-服務邊界與演進式拆分=「切在哪、怎麼切」

★ 四種架構的代價表

單體模組化單體微服務無伺服器
部署一次全部一次全部★ 獨立獨立
本地開發★ 跑一個就好★ 跑一個就好⚠️ 要跑一堆或用 mock難
跨模組重構★ 一次 commit,編譯器幫你檢查★ 同左⚠️ 跨服務的改動要協調部署順序同微服務
除錯★ 一份堆疊追蹤★ 同左⚠️ 需要分散式追蹤更難
交易★ 本地 ACID★ 同左⚠️ 分散式事務(見 工具-分散式事務的取捨)同微服務
獨立擴展❌❌★ 可以★ 自動
團隊自主❌⚠️ 有限★ 高高
技術異質❌❌★ 可以(但通常不該)可以
運維複雜度★ 低★ 低⚠️ 高中(換成廠商鎖定)

★ 最容易被忽略的是第 3、4、5 列:
微服務讓「跨邊界的改動」從「一次 commit」變成「多個 repo、多次部署、要考慮順序與相容性」。
這個成本天天都要付,而「獨立擴展」的好處多數團隊一年用不到幾次。

★ 微服務的前置條件(沒有這些就是自找麻煩)

□ ★ 自動化部署(每個服務都能一鍵、安全地上線與回滾)
□ ★ 集中式日誌 + 分散式追蹤(不然事故時完全瞎)
□ 監控與告警(每個服務都有)
□ ★ 服務發現與設定管理
□ 契約測試或 schema 相容性檢查(見 [[工具-資料編碼與演進]])
□ ★ 團隊真的能獨立負責一個服務(見 [[工具-康威定律]])

★ 沒有這六項就拆微服務 = 把「一個難維護的系統」變成「N 個難維護的系統 + 一個難懂的網路」。
→ 這正是「分散式單體」的定義:拆開了,但還是要一起部署。

★ 模組化單體:最被低估的選項

internal/
  sales/      ← 邊界清楚,只透過公開介面往來
  warehouse/
  billing/
cmd/server/   ← 一個二進位檔
你拿到了你避開了
★ 模型與團隊的邊界清晰❌ 網路呼叫的延遲與失敗
★ 編譯期就能驗證邊界(架構測試)❌ 分散式事務
★ 本地交易、本地重構❌ 部署協調
★ 需要時可以再拆出去❌ 運維成本

★ 「先模組化單體,被驗證穩定的邊界才拆出去」是截至 2026-10 業界最主流的建議(判斷,非統計),
而且它讓你用低成本試錯邊界——切錯了只要改個資料夾,不是改一個 repo。

★ 什麼時候真的該拆

拆的正當理由不是理由
★ 團隊互相擋住彼此的部署「微服務比較現代」
★ 某個部分的資源需求差異極大(ML 推論要 GPU、影片轉檔要 CPU)「想試試 Kubernetes」
★ 不同的可用性/合規要求(金流要更嚴的稽核)「檔案太大了」
不同的發布節奏(一個一天十次、一個一季一次)「聽說單體不好」
需要用不同語言(真的有技術理由)「想用 Rust」

★ 注意共同點:前四個都是「組織或營運的理由」,不是技術的理由。

★ 無伺服器的真實取捨

✅❌
零運維、自動擴縮、按用量計費★ 冷啟動(延遲敏感的服務不適合)
事件驅動天然契合★ 執行時間上限
突發流量友善★ 廠商鎖定、本地測試困難
★ 低流量時極省錢★ 高且穩定的流量時反而比 VM 貴

🧪 我實際套用的紀錄

  • 2026-07-15:(待填)

⚠️ 注意 / 什麼時候不適用

  • ★★ 不要一開始就微服務。你還不知道邊界在哪,而錯的邊界比單體更難改(跨服務重構極貴)。
  • ★ 微服務不會讓程式碼變乾淨。爛的設計拆成十個服務只會變成十份爛設計 + 網路問題。
    先把模組邊界理清楚(那在單體裡就能做)。
  • ★ 「分散式單體」是最糟的結果:拆開了,但每次發布都要按順序一起部署。
    判準:能不能只部署其中一個服務?不能 → 你只是把方法呼叫換成了 HTTP。
  • 技術異質是雙面刃。五種語言 = 五套工具鏈、五種監控接法、人員無法流動。
    ★ 多數團隊應該「刻意限制」技術選擇(見 技術選型收斂)。
  • ★ 拆分是難以逆轉的。合併回去比拆開更痛。所以要在「有足夠證據」時才拆。
  • 團隊規模是硬限制。少於 15–20 人基本不該考慮微服務——你沒有足夠的人來維運。

🔗 相關工具

⚙️ 分散式的代價 容錯 × 事務 × 分流

🎯 什麼情境該想到我

當一個下游服務變慢/掛掉,就把整條鏈路甚至整個系統拖垮(雪崩)時。

⚙️ 怎麼用(組合使用)

  1. 熔斷(Circuit Breaker):偵測到下游持續失敗就「跳閘」,快速失敗、暫停呼叫,給它時間恢復,避免堆積。
  2. 艙壁隔離(Bulkhead):把資源(執行緒池/連線)按依賴分艙,一個依賴爆掉不會吃光全部資源。
  3. 重試 + 退避:對暫時性失敗重試,但要指數退避 + 上限 + 冪等,避免重試風暴(見 工具-MQ消費端防禦三原則)。
  4. 降級(Fallback):失敗時回傳合理的預設/快取結果,保住核心體驗。
  5. 流量控制(限流):超過容量就擋,保護系統不被打垮。

★ 為什麼需要它:連鎖故障的機制

下游變慢(不是掛,只是慢)
  → 你的請求堆積,執行緒/連線被佔住
  → ★ 你自己的資源耗盡 → 你也開始變慢
  → 上游的請求在你這裡堆積 → 上游也垮
  → ★ 整條鏈一起倒

★ 最危險的不是「下游掛了」,是「下游變慢」。
掛了會快速失敗(連線被拒),資源立刻釋放;慢會持續佔住資源直到耗盡。
所以逾時是第一道防線,比斷路器更基本。

★ 五種手段的正確順序(由外而內)

限流 Rate Limit        ← ★ 最外層:擋掉超過容量的流量,保護自己
  艙壁 Bulkhead        ← 隔離資源,一個依賴爆掉不影響其他
    斷路器 Circuit Breaker  ← 下游持續失敗就快速失敗
      逾時 Timeout      ← ★ 必須在重試外層,否則重試會超出總時限
        重試 Retry      ← 只對暫時性錯誤
          實際呼叫

★ 順序錯了會失效:Retry 包在 Timeout 外面 → 總時間變成 n × timeout,SLO 直接破
(見 裝飾者模式 的 middleware 順序)。

★ 斷路器的三個狀態與參數

      失敗率超過閾值
CLOSED ──────────────▶ OPEN ──等待 cooldown──▶ HALF-OPEN
  ▲                     │                        │
  │   ★ 試探成功         │  ★ 直接快速失敗         │ 放少量請求試探
  └─────────────────────┴────────────────────────┘
                              試探失敗 → 回 OPEN

★ 它本質上是一個狀態機(見 狀態模式)。

參數怎麼定
失敗率閾值常見 50%。★ 要用「比例」不是「絕對次數」,否則低流量時永遠觸發不了
最小請求數★ 必要:10 次請求裡錯 5 次不該跳閘(樣本太小)
cooldown幾秒到幾十秒。太短 → 反覆試探打死下游;太長 → 恢復太慢
half-open 的試探量1~數個。不要一次全放行(會瞬間打垮剛恢復的下游)

★ 「什麼算失敗」要想清楚:逾時、5xx 算;4xx(參數錯)不算(那是你的問題,重試也沒用)。

★ 重試的三條鐵律

鐵律說明
① 只重試暫時性錯誤429、503、逾時、連線失敗 可以;400、401、404、422 絕對不要
② ★ 必須冪等非冪等的操作重試 = 重複下單、重複扣款。★ 帶冪等鍵(見 工具-MQ消費端防禦三原則)
③ ★ 指數退避 + jitter沒有 jitter,所有客戶端會同步醒來再打一次,製造週期性尖峰
// ★ full jitter:業界標準做法
delay := time.Duration(rand.Int63n(int64(min(base<<attempt, maxDelay))))

★★ 重試放大是連鎖故障最常見的加速器:

三層服務,每層重試 3 次 → 最底層收到 3³ = 27 倍的流量
★ 下游已經在掙扎了,你送 27 倍過去 = 徹底打死

對策:

  • ★ 只在最靠近失敗點的那一層重試,其他層直接往上回報
  • 重試預算(retry budget):全域限制「重試佔總請求的比例」(如 10%)
  • 尊重 Retry-After header

★ 降級要事先設計,不能臨時想

降級層次例子
回傳快取的舊資料★ 最常用。要標示「資料更新於 X」
回傳預設值 / 空結果推薦系統掛了 → 回熱門商品
關閉非核心功能購物車能結帳,但「你可能也喜歡」消失
★ 排隊 / 非同步化同步做不到 → 收下請求、回 202、之後處理
拒絕★ 明確的錯誤 + Retry-After,遠勝於超時等待

★ 降級開關要能手動觸發(feature flag),不要只靠自動偵測——
事故當下你需要能立刻關掉某個功能,而不是等斷路器自己判斷。

★ 艙壁隔離

❌ 共用一個執行緒池/連線池
   → 呼叫 A 服務的請求全部卡住 → 池子耗盡 → ★ 呼叫 B 服務的也沒資源了

✅ 每個下游依賴各自一個池(或 semaphore 限制併發數)
   → A 爆掉只影響 A 那一艙

★ Go 裡最簡單的實作就是每個依賴一個 semaphore(golang.org/x/sync/semaphore 或帶緩衝的 channel)。

🧪 我實際套用的紀錄

  • 2026-07-15:(待填)

⚠️ 注意 / 什麼時候不適用

  • ★★ 逾時是第一優先,不是斷路器。 沒有逾時,其他手段全部失效——因為資源永遠不會被釋放。
    而且逾時要端到端傳遞(context),不是每層各設一個(見 工具-select超時與競速)。
  • ★ 重試放大是連鎖故障的加速器(見上)。只在一層重試。
  • ★ 斷路器在「低流量」時不可靠。一小時只有 20 次請求時,失敗率的統計意義很弱。
    → 設最小請求數,或對低流量服務不用斷路器。
  • ★ 降級的品質要能被察覺。默默回傳空結果 → 使用者以為「真的沒有資料」。
    要有 metric 追蹤降級觸發率,而且降級中的回應要能被識別(header 或欄位標示)。
  • 這些機制本身會製造 bug。斷路器誤跳、重試造成重複、降級邏輯有 bug——
    ★ 每一個容錯機制都要被測試(故障注入,見 工具-注入故障與韌性演練)。
  • 不要對所有依賴都套用全套。核心依賴掛了就是該失敗;只有「可以沒有」的依賴才值得降級。
    → 先分清楚關鍵路徑與非關鍵路徑。
  • 用成熟的函式庫(sony/gobreaker、failsafe-go、Envoy/Istio 的 outlier detection),不要自己寫。

🔗 相關工具

🎯 什麼情境該想到我

當一個業務動作要跨多個服務/資料庫、卻無法用單機交易保證「要嘛全成、要嘛全敗」時。

⚙️ 怎麼用(依一致性需求選方案,沒有銀彈)

  1. 可靠訊息佇列(本地訊息表 = Outbox):把業務寫入與事件記錄放同一本地交易,再由 relay 投遞 → 最終一致。最常用、最務實(見 工具-Transactional-Outbox-Pattern)。
  2. TCC(Try-Confirm-Cancel):業務層自己實作預留/確認/取消,強一致但侵入性高、開發成本大。
  3. Saga:把長交易拆成一連串本地交易 + 對應的補償動作,失敗就反向補償。適合長流程。
  4. 2PC/XA:強一致但同步阻塞、協調者單點,效能差,少用於高併發。

先問:這個場景需要強一致,還是最終一致就好?多數選最終一致(1)。

★ 先問三個問題(多數情況根本不需要分散式事務)

① ★ 這兩件事真的必須在「不同的服務/資料庫」嗎?
   → 常常是切錯了邊界。同一個聚合內的東西不該跨服務(見 [[工具-限界上下文]])

② ★ 真的需要「強一致」,還是「最終一致 + 使用者看得到狀態」就好?
   → 「下單後 3 秒庫存才扣」在業務上幾乎總是可以接受的

③ ★ 失敗時可以「人工處理」嗎?
   → 每天 3 筆異常 → 對帳報表 + 人工補償,★ 遠比建一套 Saga 框架划算

★ 最好的分散式事務是「不要有分散式事務」。
大多數「需要分散式事務」的需求,實際上是服務邊界切錯了的症狀。

★ 四種方案的完整對照

方案一致性複雜度可用性★ 什麼時候用
★ Outbox(本地訊息表)最終低高★ 預設選這個。90% 的場景
Saga最終中高長流程、多步驟、有明確的補償動作
TCC準強高中需要「預留資源」的場景(訂票、扣庫存)
2PC / XA強中★ 低幾乎不要用(見下)

★ Outbox:為什麼它是預設答案

❌ 雙寫問題(dual write)
BEGIN; UPDATE orders ...; COMMIT;
kafka.Publish(event)          ← ★ 這裡掛掉 → 訂單建了但沒人知道

✅ Outbox
BEGIN;
  INSERT INTO orders ...;
  INSERT INTO outbox (event) VALUES (...);   ← ★ 同一個本地交易,要嘛都成功要嘛都失敗
COMMIT;
-- 背景 relay(或 CDC 讀 WAL)把 outbox 推到訊息佇列

★ 它的天才之處:把「跨系統的原子性」化約成「單一資料庫的本地交易」,
而單一資料庫的交易是可靠的(見 工具-交易與隔離等級)。

代價:至少一次投遞 → ★ 消費端必須冪等(見 工具-MQ消費端防禦三原則)。

★ Saga:補償的設計才是難點

正向:  建訂單 → 扣款 → 扣庫存 → 出貨
補償:  取消訂單 ← 退款 ← 回補庫存 ← (已出貨無法補償)

三件必須想清楚的事:

問題說明
★ 補償不等於回滾退款 ≠ 沒扣過款(帳上留下兩筆紀錄、可能有手續費)。
Saga 沒有「原子性」,只有「最終達到某個可接受的狀態」
★ 有些步驟不可補償已出貨、已寄出的 email、已呼叫的第三方 API。
→ ★ 把不可逆的步驟排在最後,或改成「可逆的形式」(延遲執行、草稿)
★ 補償本身也會失敗退款 API 也可能掛。→ 補償必須可重試 + 冪等 + 最終有人工介入的出口(dead letter)

編排(orchestration)vs 協同(choreography):

OrchestrationChoreography
流程在哪★ 一個中央協調者(看得到整條流程)散在各服務的事件監聽裡
好處★ 流程可讀、可追蹤、易改鬆耦合,沒有中心
壞處協調者是新的耦合點★ 沒有人知道完整流程長什麼樣
建議★ 步驟 ≥ 3 就用編排只有兩步時可以協同

★ 為什麼 2PC 幾乎不該用

協調者 → 「準備好了嗎?」→ 所有參與者鎖住資源並回「好了」
       ★ 此時協調者掛掉 → 所有參與者「永遠持鎖等待」(in-doubt)
       → 資源被鎖住,無法自行決定,必須等協調者回來
問題說明
★ 協調者單點它掛了,參與者全部卡住
★ 同步阻塞整個 prepare→commit 期間所有參與者持鎖
可用性差任一參與者不可用,整個交易失敗(可用性是各方的乘積)
效能差至少兩輪網路往返 + 持鎖時間長

★ 它在單一資料庫叢集內(同一個 DBMS 的多個分片)還算堪用;跨異質系統基本上是災難。

★ 產品端要配合的事(技術解不了的部分)

最終一致性必須在產品層面被承認,不能偷偷做:

  • ★ 顯示中間狀態:「訂單處理中」而不是假裝已完成
  • ★ 提供查詢入口:使用者能看到目前進度
  • 定義補償後的使用者體驗:退款要通知、要說明原因
  • ★ 對帳機制:定期比對兩邊,找出不一致並告警——
    這是最被忽略但最重要的一環。沒有對帳,你永遠不知道系統有沒有出錯。

🧪 我實際套用的紀錄

  • 2026-07-15:(待填)

⚠️ 注意 / 什麼時候不適用

  • ★★ 先檢討服務邊界。頻繁需要跨服務事務 = 邊界切錯了(見 工具-服務邊界與演進式拆分)。
    合併回同一個服務往往是正確答案,而不是蓋一套 Saga 框架。
  • ★ 一定要有對帳。任何最終一致的系統都會飄移,只是你知不知道。 排定期對帳 + 告警 + 人工修補流程。
  • ★ 消費端冪等不是可選的。所有方案都是「至少一次」投遞。
  • 不可逆的步驟要排在最後,或延遲執行(見 可逆性、審計與人類把關 的 L3 降級技巧)。
  • Saga 的狀態要持久化。流程執行到一半服務重啟 → 要能從中斷處續跑(見 狀態模式)。
  • 不要用「分散式鎖」代替分散式事務。鎖不安全(見 工具-線性一致性與共識 的 fencing token)。
  • ★ 測試很難。要測「第 3 步失敗時補償有沒有正確執行」——故障注入是必要的(見 工具-注入故障與韌性演練)。
  • 監控要能看到「卡住的 Saga」:超過 N 分鐘還沒完成的流程要告警。

🔗 相關工具

🎯 什麼情境該想到我

當你在設計「使用者請求如何一路被導到正確、健康的服務節點」,或思考 CDN/負載均衡/網關該怎麼擺時。

⚙️ 怎麼用(一層層把流量導到對的地方,越前面擋掉越多越好)

  1. DNS:把網域解析到最近/可用的入口。
  2. CDN:靜態內容就近快取,別讓請求都打到源站。
  3. 負載均衡:把流量分散到多個健康節點(四層/七層)。
  4. API 網關:統一入口,做認證、限流、路由、聚合。
  5. 服務發現:讓呼叫端動態找到可用的服務實例(配合健康檢查)。

原則:能在越外層解決/擋掉的流量,就別讓它進到越內層,層層減壓。

★ 完整的分流鏈(越前面擋掉越便宜)

瀏覽器快取      ← ★ 最便宜:連請求都不發出
  ↓
DNS / GSLB      ← 地理就近、機房級故障轉移
  ↓
CDN / 邊緣快取  ← ★ 靜態資源與可快取的 API 不進源站
  ↓
WAF / DDoS 防護 ← 惡意流量在這裡被擋
  ↓
L4 負載均衡     ← 四層:只看 IP:Port,極快
  ↓
L7 負載均衡/API 閘道 ← ★ 認證、限流、路由、聚合
  ↓
服務(+ 服務發現、服務網格)
  ↓
應用快取(Redis)
  ↓
資料庫(+ 讀取副本、連線池)

★ 核心原則:「每往內一層,成本大約高一個數量級。」
一個能在 CDN 命中的請求,成本大約是打到資料庫的千分之一。
所以架構優化的第一個問題永遠是:「這個請求能不能在更外層就被解決?」

★ L4 vs L7 負載均衡

L4(傳輸層)L7(應用層)
看得到什麼IP、Port、TCP 連線★ HTTP 方法、路徑、header、cookie
能做什麼依連線分流★ 依路徑路由、依使用者黏著、重寫、壓縮、TLS 終止
效能★ 極高(可以只改封包不拆解)較低(要解析 HTTP)
健康檢查TCP 連得上就算活★ 能打 /healthz 看真實狀態
例子LVS、AWS NLBNginx、Envoy、AWS ALB

★ 實務上常兩層都有:L4 做第一層分散與 DDoS 吸收,L7 做智慧路由。

★ API 閘道:該放什麼、不該放什麼

✅ 該放(橫切關注點)❌ 不該放
認證與 token 驗證★ 業務邏輯
限流與配額(見 工具-限流器設計)★ 資料轉換與聚合的複雜邏輯
路由與版本需要頻繁改動的東西
請求/回應日誌與 trace 注入任何「屬於某個團隊」的規則
TLS 終止、壓縮、CORS

★★ API 閘道最大的風險:它會變成新的單體。
每個團隊都想在閘道加一點邏輯 → 它變成所有人都要改、誰都不敢動的中心元件,
而且它掛了全站都掛。
→ 閘道只放「所有服務都一樣」的東西;只要有「某個服務特別」的邏輯,就該放回服務裡。

★ 快取的層次與失效難度

層命中的價值★ 失效的難度
瀏覽器最高(零請求)★ 最難(你無法主動清除別人的瀏覽器)
CDN很高中(可以 purge,但有延遲)
閘道/反向代理高中
應用內(Redis)中★ 最容易(你完全控制)

★ 這張表解釋了一個重要的取捨:
越外層越省,但也越難改。
→ 內容會變的東西用短 TTL 或放內層;不會變的(帶 hash 的靜態資源)才用長 TTL 放最外層。
→ 靜態資源用 content hash 檔名(app.a3f1c2.js)+ 永久快取,是這個取捨的標準解法。

★ 服務發現

方式例子
DNS★ 簡單,但有快取與 TTL 問題
註冊中心Consul、etcd、Eureka
★ 平台內建Kubernetes Service(多數情況的正解)
服務網格Istio / Linkerd —— ★ 把重試、逾時、mTLS、追蹤下沉到 sidecar

★ 服務網格的取捨:它把容錯邏輯從應用抽出來(好),但多一層代理的延遲、更多的運維複雜度、更難除錯(壞)。
服務數量少於一二十個時,通常不值得。

🧪 我實際套用的紀錄

  • 2026-07-15:(待填)

⚠️ 注意 / 什麼時候不適用

  • ★★ 每一層都是一個可能的單點。加一層 = 多一個會掛的東西、多一跳延遲、多一個要監控的對象。
    不要為了「架構完整」而把每一層都加上。
  • ★ 快取失效是最難的部分。加快取容易,「什麼時候該讓它失效」才是問題。
    沒想清楚就加 → 「資料明明改了但使用者看到舊的」(見 工具-快取與資源優化)。
  • ★ 閘道不要變成單體(見上)。
  • DNS 的 TTL 不可靠。很多解析器會忽略你設的 TTL → 不要依賴 DNS 做快速故障轉移。
  • TLS 終止的位置決定了內網是否加密。合規要求端到端加密時,不能只在邊緣終止。
  • 健康檢查要檢查「真的能做事」,不是只回 200(見 工具-故障偵測)。
  • 小系統不需要這麼多層。一個 Nginx + 應用 + 資料庫能撐很久。先量再加。

🔗 相關工具

Link to original

🧰 這本書給我的工具

分散式事務、容錯、分流等與 鳳凰架構 共用:工具-分散式事務的取捨、工具-服務容錯設計、工具-透明多級分流、工具-服務架構的演進與拆分。

✨ 關鍵重點(Layer 1–2)

  • 邊界對齊業務:用 工具-限界上下文 找切點,而非按技術分層拆。
  • 康威定律:系統架構會反映組織的溝通結構 → 想要好架構,先設計好團隊。
  • 微服務不是免費的:換來獨立部署/擴展,代價是分散式複雜度、運維、資料一致性。
  • 演進式:先單體、痛點出現再拆;每個服務獨立部署、擁有自己的資料。
  • 高內聚低耦合:能獨立變更與部署,是好服務邊界的檢驗。

💬 金句原文(Layer 0)

  • 「微服務帶來的好處,都伴隨著相應的複雜度成本。」

🔗 相關