微服務架構 #2, 按照架構,重構系統
摘要提示
- 重構優先: 在微服務化前先進行程式碼重構,改善內聚與耦合,降低未來服務化風險與成本。
- 模組化觀念: 微服務是更強的模組化,從 code 層級提升到網路邊界,要求更嚴謹的結構設計。
- 重構目標明確: 先定義重構目的與目標架構,再依序執行重構、服務化與驗證。
- Factory + Proxy: 以 Factory 與 Proxy 模式建立抽象邊界,為未來替換實作與遠端呼叫預留彈性。
- 會員機制演進: 從單一系統內嵌 → 共用函式庫 + 共用 DB → 獨立會員服務 + API/SDK。
- 低耦合高內聚: 把登入相關邏輯集中於獨立 DLL,對外僅暴露抽象介面,內部封裝實作細節。
- 無痛切換: 以工廠回傳不同實作(Local/Remote),讓呼叫端程式碼零變更完成服務化。
- SDK 封裝: 以 SDK 包裝 Web API,降低各平台直接處理 HTTP 的複雜度與重複成本。
- 雙重驗證: 在 Debug 模式同時跑 Local 與 Remote 實作比對結果,以 Debug.Assert 早期偵錯。
- 單元測試互補: Debug 驗證與單元測試互補,分別在 runtime 與測試階段提高品質防線。
全文重點
本文延續微服務架構的討論,聚焦「如何做」的實作路徑。作者主張:在將單體式系統切為微服務之前,務必先完成程式碼重構,因為微服務是更嚴苛的模組化,若原始體質欠佳,服務化後的分散式除錯成本與維護難度將大幅提升。重構的關鍵在於先釐清「重構的目的」,規劃欲達成的架構與服務邊界,再依序進行程式碼重構、建構服務與驗證轉移結果。
文章以常見的會員系統為例,說明從內嵌在單體應用的散落程式碼,逐步提煉為獨立模組、再演進成獨立服務的過程。作者提出一條務實演進路徑:當只有一個系統使用時,會員機制可能直寫於應用內;當有少數系統共用時,抽出標準化函式庫並共用資料庫;當更多系統需要共用時,則升級為獨立會員服務(擁有獨立服務與資料庫)並公開標準 API,輔以 SDK 降低呼叫成本。
在重構階段,作者強調以「低耦合」為優先,採用 Factory + Proxy 設計模式建立抽象界面(如 LoginServiceBase),將登入機制的邏輯集中於 DLL,對外僅暴露抽象方法。如此一來,將來只需於 Factory 切換實作(LocalDatabaseService → RemoteLoginService),即可無痛把呼叫端導向遠端服務,達成「呼叫端零變更」的服務化轉移。這同時符合開放封閉原則:對擴充開放、對修改封閉。
服務化實作部分,作者以 ASP.NET Web API 示範最小登入 API,並在 SDK 端以 RemoteLoginService 封裝 HTTP 呼叫與參數處理,讓客戶端程式仍透過相同抽象類別呼叫,維持一致使用體驗。為確保轉移正確,作者引入「雙重驗證」技巧:在 Debug 模式下,同步執行本地與遠端兩個版本並比較結果(Debug.Assert),藉此在開發期即快速暴露行為差異與潛在缺陷。作者指出,這種方法靈感源自「Writing Solid Code」一書,雖增加計算成本,卻能換取早期錯誤回報與除錯效率;同時強調此法與單元測試互補,而非替代。
總結來說,作者提出一套從單體到微服務的「先重構、再服務化、再驗證」的漸進式實作策略,核心是以設計模式建立抽象邊界、以 SDK 降低整合摩擦、以雙重驗證提升轉移品質。這種做法不追求一步到位,而是以可持續的架構與工程紀律,降低風險、提升可維護性,並為後續服務邊界的決策打好基礎。下一篇將進一步討論如何決定要切割哪些模組為獨立服務。
段落重點
一定要做的事: 程式碼重構
作者先破題:微服務本質是更高階的模組化,若原始程式碼結構鬆散,直接服務化將使問題更難定位、除錯成本倍增。重構前需先釐清目的與預期架構,避免技術債帶入分散式場景。作者提出四步驟:架構設計(確定要獨立的模組與服務邊界)、程式碼重構(以 Factory + Proxy 拉出抽象邊界,達高內聚低耦合)、建構服務(抽換為遠端代理與工廠配置,平滑導入網路呼叫)、驗證轉移(以單元測試與雙重驗證確保一致性)。以會員登入為例,若登入與驗證散落各處,應先封裝為模組,再談服務化。作者提醒:微服務要求結構正確性,否則規模擴張後會陷入維護地獄。
STEP 1, 決定架構,訂定重構的目標
許多系統初期為了上市時程而忽略架構,隨著需求增長必須逐步償還技術債。作者將會員機制的演化分為三階段:單系統內嵌;少量系統共用時抽成函式庫並共用資料庫;大量服務共用時獨立為服務,擁有獨立資料庫與標準 API。重構前應先設定目標:當前要達到哪一階段?並預留通往下一階段的擴充性,避免日後「打掉重練」。此即重構的目的性:不是為重構而重構,而是為了逐步達成合理服務邊界與演進路徑。
STEP 2, 重構目標 - 模組化
從內嵌登入邏輯進入模組化階段的關鍵是:低耦合優先、高內聚落地。作者以 LoginServiceBase 抽象類別示範:以 Factory 封裝實作取得(例如僅回傳 LocalDatabaseService),把密碼雜湊、驗證流程與 Token 建立集中於 DLL,對外僅暴露必要的抽象操作。這帶來三個效益:一是「唯一入口」確保一致性與可控性;二是透過抽象屏障,呼叫端只面對穩定介面;三是為未來替換為遠端實作鋪路。此階段達成了(2)函式庫共享的目標,並符合開放封閉原則:不動呼叫端,允許新增實作。
STEP 3, 重構目標 - 服務化
當進入(3)多服務共用時,會員機制應獨立為服務,擁有自己的服務與資料庫,並以標準 API 對外。作者以 ASP.NET Web API 範例展示最小登入端點,再以 SDK 封裝 HTTP 細節(RemoteLoginService 繼承抽象基底),對外仍維持相同呼叫方式。落地策略是「工廠切換」:僅在 Factory 改由 RemoteLoginService 產生實例,呼叫端原始碼完全不變,即可完成導流至遠端服務。SDK 的角色是降低跨平台直接處理 HTTP 的門檻,提升整合體驗與一致性。
STEP 4, 確保服務化過程的正確性
微服務是分散式系統,錯誤可能來自主系統、外部服務或網路,除錯常需跨多服務與網路追蹤。為降低風險,作者採用「雙重驗證」:在 Debug 模式同時執行本地與遠端兩個實作,逐一比對結果(Debug.Assert)。此法源自「Writing Solid Code」:先有正確但笨的版本,再逐步最佳化;在 Debug 下同時跑兩版,比對不一致即警示。好處是第一時間揭露偏差,大量節省追錯成本。作者強調,這並非取代單元測試,而是互補——單元測試在開發/測試期,雙重驗證在實際執行路徑中捕捉意料之外的行為差異。
總結: 切割為微服務的實作案例
作者的實作哲學是「先重構、再服務化、再驗證」,以設計模式建立抽象邊界、以 SDK 降摩擦、以雙重驗證保品質。這套方法特別適合從大型單體式系統演進的情境,能以最小風險穩健上路,而非一次到位的激進重寫。文章並非否定其他方法,而是基於多次實作後的可靠路徑分享。後續將討論更上層的課題:如何正確劃分服務邊界、哪些模組值得獨立,作為本篇重構與服務化實務的延伸。
資訊整理
知識架構圖
- 前置知識:
- 物件導向設計原則(高內聚/低耦合、單一職責原則、開放封閉原則)
- 設計模式基礎(Factory、Proxy)
- 單元測試與斷言(Unit Test、Debug.Assert)
- Web API 基礎與用戶端呼叫(HTTP、SDK 包裝)
- 分散式系統的基本挑戰(網路不穩定、跨服務除錯成本)
- 核心概念:
- 重構先於服務化:先整頓程式碼體質,再切服務,降低風險與成本
- 模組化到服務化的演進:單體內嵌 → 共用函式庫 → 獨立服務+API+資料庫
- 設計模式支撐解耦:以 Factory 抽換實作、以 Proxy/Remote Proxy 轉向遠端
- API/SDK 邊界:以抽象介面(abstract/interface)界定用法,SDK 降低使用門檻
- 雙重驗證機制:Debug 模式同時跑 Local 與 Remote,比對結果(Debug.Assert)確保一致性
- 技術依賴:
- RemoteLoginService 依賴 Web API(HTTP)與序列化;LoginServiceBase 依賴 Factory 產生適當實作
- 應用程式僅依賴抽象(LoginServiceBase),不依賴具體實作(Local/Remote)
- 測試與驗證依賴單元測試與 Debug 斷言;服務品質依賴 SDK 封裝減少直接 HTTP 操作
- 應用場景:
- 既有單體式系統逐步導入微服務
- 多個系統需要共用會員/登入等橫切關注點
- 重構過程中要降低改動風險並保持對外行為不變
- 要在開發期快速偵測與定位服務化引入的不一致問題
學習路徑建議
- 入門者路徑:
- 學會物件導向原則與基本設計模式(Factory、Proxy)
- 練習將分散的商業邏輯重構為高內聚、低耦合的模組(抽象類別/介面)
- 了解基本的 Web API 呼叫與用戶端封裝(HTTP、JSON、SDK 範例)
- 進階者路徑:
- 將模組演進為可抽換的實作(Local vs Remote),以 Factory 隔離建構邏輯
- 設計穩定的模組介面與 API 契約(參數、回傳、錯誤碼、雜湊規格等)
- 導入單元測試與 Debug 雙重驗證機制,建立持續整合檢查
- 實戰路徑:
- 從單體抽出目標模組(如會員),做成共用 Library,僅暴露抽象介面
- 新增 Remote 版本(Web API + 專屬資料庫),並提供對應 SDK
- 以 Factory 切換至 Remote 實作;在 Debug 模式啟用 Local/Remote 雙跑比對
- 逐步將其他模組複製此策略,建立服務邊界與部署
關鍵要點清單
- 重構先行: 微服務化前先改善程式碼體質,降低分散式除錯成本 (優先級: 高)
- 高內聚低耦合: 模組內聚焦單一責任、對外只經由抽象介面互動 (優先級: 高)
- Factory 模式: 以工廠抽換 Local/Remote 實作,達成對修改封閉、對擴充開放 (優先級: 高)
- Proxy/Remote Proxy: 在同一抽象後面切換遠端呼叫,對呼叫端零侵入 (優先級: 高)
- 介面/契約穩定性: 包含密碼雜湊方式、參數與回傳的規格需穩定且文檔化 (優先級: 高)
- 三段式演進: 單體內嵌 → 共用函式庫 → 獨立服務+資料庫+API (優先級: 高)
- SDK 封裝: 以平台友善的 SDK 降低直接操作 HTTP 的負擔 (優先級: 中)
- Debug 雙重驗證: Debug 模式同時跑 Local/Remote,比對結果用 Debug.Assert (優先級: 高)
- 單元測試: 以 Unit Test 保證行為一致性,與 Debug 斷言互補 (優先級: 高)
- 開放封閉原則: 現階段為未來服務化預留擴充點而不過度預先實作 (優先級: 中)
- 服務邊界思維: 雖本文聚焦重構,但需意識未來邊界切分與資料歸屬 (優先級: 中)
- 降低呼叫端改動: 以 Factory 切換實作,呼叫端程式零修改即可遷移 (優先級: 高)
- 分散式風險管理: 認知網路不穩定、跨服務追蹤、流量等問題並預做觀測 (優先級: 中)
- 技術債遞延清償: 由重構逐步償還技術債,避免服務化後放大問題 (優先級: 高)
- 可持續演進: 設計選擇需能平滑過渡,避免未來「打掉重練」(優先級: 中)