在 Microservice Architecture 的第二個層次是「Application Infrastructure Patterns」,在這一層次討論橫切面關注點 (cross-cutting concerns)、安全、通訊模式、外部 API 與服務發現等。你會發現,這些已經脫離了「微服務」本身的主要業務,開始以「技術」來進行相關的討論與描述。
從系統架構的角度來看,微服務本身就是一種「分散式」應用架構,所以無論是在「通訊」或是「容錯」的工作上都有相當多的討論議題 (當初我唸書的時候從作業系統到分散式作業系統這兩個轉換就知道問題複雜了好幾倍)。所以,在這個層次我們可以看到很多為了分散式架構存在的架構模式。
拆解「微服務」並不是將一個系統拆解後獨立部署到不同的伺服器上就是完成相關的工作,我們必須用這些基礎設施模式讓原本聚在一起的能力,仍能透過標準化的通訊、治理與觀測協同運作。換句話說,沒有這一層,微服務只是一堆彼此不可靠的小程式。
最早在看到這個 cross-cutting concerns 的時候怎麼查翻譯就是「橫切關注點」,一直很難想到一個自己覺得比較可以說服自己的白話文,然後再往下延伸他底下的三個模式後,我心裡面浮現的是「汽車」製造的時候都有一種共用底盤的概念,而這裡應該就是指那個基座(這也是每個客戶問我最後我會產出些什麼,對於應用程式的部分我們會為您產生一個樣板)。
在微服務的領域,如果建立一個新服務若要先花 1–2 天搭建其基礎建設 (要使用哪些套件),乘上「數十到上百個服務」會變成巨大浪費 (雖然,微服務說每一個服務都可以技術自主,但也不是每天都有新的 Idea,所以建立服務樣板來降低浪費)。
服務樣板就是把這些重複且易出錯的基礎建設一次封裝,讓團隊在短時間內就能產出一個「可部署、可觀測、可治理」的新服務。
舉例:
對 Spring Boot/Maven 而言,可用 Maven Archetype 或 Spring Initializr 自動產生的專案骨架(含最小目錄結構、基本 Dockerfile、CI 工作流程範本)——這就是 Service Template。樣板應「越薄越好」,把共通能力交給底盤處理,避免日後大量回填。
Microservice chassis 是把建置邏輯封裝成一套可重用、具備版本管理的程式庫/框架層(例如 language 專用的 SDK、Starter、Middleware),讓每個服務只需加入一個依賴就擁有一致的基建能力;相較「Service Template」的複製貼上,Microservice chassis 以發版升級統一維護,避免在數十上百個服務中逐一改檔。
Microservice chassis 讓團隊用「加一個依賴」就擁有安全、觀測、可靠通訊等基建;以版本化治理取代複製貼上,讓上百個服務既一致又好升級。
Chassis ≈ 以相依套件為載體的共用底盤(不只是加入幾個依賴)
在 pom.xml 加入的不是任意相依性,而是組織提供/維護的底盤套件(可包含多個模組),透過版本升級統一治理。(不然,共用模組一改,如果是以複製貼上模式,這些檔案就需要所有微服務團隊下來協助修正了)!
現代化的應用程式通常依賴多種基礎設施(資料庫)與第三方服務(金流、簡訊/Email等 API)。若把這些位址、憑證、金鑰、功能開關寫死在程式或映像檔中,將導致環境綁死、更新困難、風險升高。將這些參數作為外部設定檔,是把設定從程式碼與映像抽離,於部署環境注入,達到「同一套程式,跨環境可配置」。
這概念也不是微服務的時代才有,就把它當作一個 Check list 來看,提醒自己避免設計上忘記該這麼做。
在 Microservice 架構下 API 是服務之間通訊的基礎,為了保護 API 存取的安全,Access Token 可以用來做「身份驗證與授權」從入口一路安全地帶到下游服務,實現零信任的鏈路驗證與授權。
運作流程
實作要點
- 優先用 JWT(自含、可離線驗證),配合 JWKS 旋轉金鑰;高敏情境可用 opaque token + introspection。
- 最小權限:依資源定義 scope/audience,避免全能 Token。
- 短效期+可替換:短 TTL,Refresh Token 只給受信任的客戶端;服務間可用 Client Credentials。
- 防護與觀測:結合 mTLS/Rate limit/CORS;在日誌與追蹤中關鍵欄位遮罩。
Access Token 讓「誰、能做什麼」成為可驗、可授權、可追蹤的連續性憑證,使 Gateway 與各服務在分散式環境中一致而安全地協作。
都說微服務是一種「分散式」架構,所以模組跟模組之間的通訊,也是在做微服務設計時所關注的一個主要議題。一般來說,可分出三種常見的溝通模式:
總體來說:RPC 即時、Messaging 解耦、Domain-specific 貼近語意。實務上多為混用——熱路徑查詢走同步 RPC,跨域流程與整合走事件/訊息,而網域特化的協定負責把「要說的話」定義清楚,三者共同支撐可靠、可演進的分散式系統。
在微服務中,服務彼此同步呼叫時,下游當機或高延遲會讓呼叫端卡住(線程/連線被占用),進而資源耗盡、把故障連鎖放大。Circuit Breaker 透過「快速失敗」與「試探恢復」保護呼叫端與整體穩定性:
保住呼叫端資源、縮短失敗路徑時間、避免雪崩,讓系統在局部故障時仍可優雅退化並自我恢復。
在微服務中經常搭配事件驅動架構 (可能會使用 Kafka 作為引擎),在這個情節裡訊息中介常採 至少一次投遞(at-least once delivery),確保出錯也不遺失訊息;副作用是同一訊息可能被重送。因此消費者必須冪等:同一訊息處理一次或多次,結果完全相同。否則會出現如重複扣款、餘額計算錯誤等問題(例如對 AccountDebited 重覆相減)。
因此,Idempotent Consumer 主要的概念就是要我們過濾掉那些「重送」的訊息。
Idempotent Consumer 讓「可能重送」變得無害,是在事件/訊息驅動系統中維持資料正確性的基本防線。
在微服務的架構中,資料的處理模式是「最終一致」,但這就要仰賴服務與服務之間的通訊可靠度。Transactional Messaging 模式基本上就是想處理這類型的問題,讓資料寫入與訊息通知成為一件可靠的事。
試想一個場景:
有一個應用先把本地資料寫進資料庫,然後發一則訊息(事件)給其他服務。若兩步驟不被資料庫 ACID 的特性所保護,就可能出現:
• 寫資料庫成功、訊息沒發 → 下游永遠不知道發生了什麼
• 寫資料庫庫失敗、訊息卻發了 → 下游做了不該做的事
Transactional Outbox(交易型寄件匣)
運用資料庫 ACID 的特性,把要對外發布的事件,跟業務資料一起寫進同一個資料庫中 (可以做一個 Outbox 的 Table 儲存事件),如此就可以確保資料與事件都有 commit 成功。
當業務作業完成後,再將 outbox 中未發佈的事件丟到訊息系統中,並標記已發送。
Transaction Log Tailing(擷取交易日誌)
不改應用程式,在資料庫層訂閱變更日誌(例如 CDC),把已提交的資料變更轉成事件,再由 CDC 將這些變更資訊轉換成事件發佈到訊息系統。
透過此方式,就不需要異動應用程式,主要由資料庫運作的機制協助完成相關機制。
Polling Publisher(輪詢發布者)
若無法用 CDC,也不想異動既有程式,可用背景程序定期掃描資料庫的 outbox/狀態欄位,批次發布事件,發送成功後標記狀態,失敗則重試/恢復原本狀態。
在微服務裡,對外 API 常用「雙層」模式:API Gateway 做入口治理,BFF(Backends for Frontends) 負責每種前端的專屬聚合與轉譯。兩者搭配,既集中安全與流量控管,又保留前端體驗的彈性。
API Gateway
API Gateway 是 API 的單一入口,所有的南北向 API 應該都集中在這邊被管理。通常,一個 API Gateway 會具備下列職責:
目前市場上有許多 API Gateway 平台,有商用軟體也有開源軟體,實際情況可以依據自身需求調配(後面如果有需要,我再來整理一個我至今遇過的 API Gateway 解決方案,不過應該塞不進這 30 天中)。
Backend for Frontenr (BFF)
基本上,一個 BFF 層就是為前端量身打造的 API,其在架構上的職責是 (在客戶說前後端分離的狀態下,我通常都會放一個 BFF 層來處理):
1.依前端類型(Web、Mobile、Partner)聚合多個微服務資料,裁剪欄位、壓縮往返
2.轉譯模型、分頁/排序、拼裝視圖;封裝前端特有需求
3.在不影響後端領域模型的前提下,快速演進 UI
API Gateway 管治與安全,BFF 管體驗與聚合;兩者分工,讓外部 API 同時「好管、好用、好演進」。
如果有仔細看 Pattern Language 那張圖的話,你應該會發現,我又沒把 Application Infrastructure Patterns 在這一篇解決掉。眼尖的應該知道我把「可觀測性」閃掉了!
因為在我自己的經驗中,「可觀測性」可能是導入微服務應用中也是討論比較久的議題 (在我遭遇的案例中),所以,我就把它再往下一章取得有一定的篇幅跟關注力的時候來談這件事。
在這個篇章裡面,我們也交代許多「Application Infrastructure Patterns」,我們來快速複習一下:
累積的資訊量其實也不少,所以本篇章就暫時到這邊打住,看來有點太小看每個議題的內容,要交代這些事情寫著寫著就發現怎麼塞了那麼多資訊。