2026 年企業核心系統微服務化拉開序幕:Strangler Fig 絞殺者模式實作
@Com
// 新實作:呼叫獨立部署的定價服務
@Com
} c
c
以下是一套經過多次實戰修正的六階段路線圖。原則是:每一階段結束時,系統都必須處於可上線、可回滾的狀態。
4.1 第 0 階段:辨識絞殺縫隙(Seam Identification)
縫隙(Seam)是系統中「可以插入切換點而不需修改內部結構」的位置。找出好的縫隙,是整個專案最重要的一次決策。評估一個候選縫隙的標準有四項:
變更頻率:業務上是否經常需要修改?越常改的越值得先拆。
資料獨立性:它主要操作的資料表,是否與其他能力有大量共享寫入。
風險容忍度:出錯時的業務影響是「客人多等兩秒」還是「帳務錯亂」。
實務建議:第一個絞殺目標應該選「高變更頻率、低資料耦合、中低業務風險」的能力。例如「會員等級查詢」、「商品目錄」、「通知偏好設定」。第一個目標的意義不在於它本身有多大價值,而在於它必須成功,用來建立組織信心與驗證整條工具鏈。把它當成一次完整的實彈演習。
4.2 第 1 階段:建立門面與旁路
此時不做任何功能遷移,只做兩件事:
建立觀測基礎:統一的 correlation-id 傳遞、門面層的存取日誌、以及路由決策的事件記錄。
這一階段常被視為「沒有產出」而遭刪減,但它其實是在為後續所有階段購買保險。門面一旦上線並穩定運行一個月,後續的每一次切換都只是一行設定變更。
4.3 第 2 階段:讀取路徑絞殺(Read Strangler)
新服務先接管查詢流量,但寫入仍由單體負責。資料透過 CDC 單向同步到新服務的讀取模型。
這個階段的價值在於:
驗證資料同步的正確性與延遲特性(同步延遲是否在業務可接受範圍內,例如 500ms 內)。
驗證新服務在真實流量下的效能與穩定性。
建立團隊對新部署流程與工具鏈的肌肉記憶。
切換策略建議採用分層放量:先內部使用者、再 1% 真實流量、再 10%、再 50%、再 100%。每一層至少要觀察一個完整的業務週期(例如包含一個週末與一個月初結帳日)。
4.4 第 3 階段:寫入路徑絞殺(Write Strangler)
這是真正的分水嶺,因為它涉及資料主權的移轉。建議採用命令攔截 + 反向同步的過渡架構:
門面把寫入請求導向新服務。
新服務在自己的資料庫完成寫入,並發布領域事件。
一個適配器訂閱事件,把變更以舊系統能理解的形式寫回單體資料庫。
單體仍能讀到最新資料,因此所有尚未絞殺的功能不受影響。
這個架構讓你可以逐功能切換寫入,而不必一次移轉整張資料表。但要注意:反向同步必須對「單體自己也會修改同一筆資料」的情況有防禦機制,否則兩邊互相覆寫會產生極難追查的資料損壞。常見做法是對資料列加上版本號或 last_modified_by 標記,並在衝突時採「新服務優先」或「人工裁決佇列」策略。
4.5 第 4 階段:資料主權正式移轉
當某個能力的所有讀寫路徑都已由新服務承接,且反向同步已穩定運行足夠長的觀察期,就可以進行正式移轉:
停止反向同步。
在單體中把該能力的相關程式碼改為「呼叫新服務」或直接移除。
針對單體中殘存的直接資料庫存取(例如報表、批次作業),建立唯讀檢視或資料倉儲同步。
這裡最容易被忽略的是非同步的資料消費者:批次對帳程式、BI 報表、資料倉儲 ETL、稽核歸檔。它們往往直接連線單體資料庫,不會出現在 API 呼叫圖上。務必在移轉前做一次完整的資料血緣盤點。
4.6 第 5 階段:掏空與退役
重複上述流程,直到單體只剩下一層薄殼。此時才決定它的最終命運:完全下線、或保留為一個極小的核心服務。無論哪一種,都要記得把絞殺過程中累積的臨時性構件清理乾淨:影子比對程式碼、雙寫適配器、特性開關、已無用的唯讀檢視。這些臨時構件如果不清,會變成下一代的技術債。
五、常見陷阱與反模式
5.1 陷阱一:門面變成新的單體
症狀:門面開始出現 if (customer.type == "VIP" && order.amount > 10000) 這類條件判斷。原因通常是某個邊界情境用路由規則難以表達,工程師就「暫時」把邏輯放進門面。
防禦方式:把「門面不得包含業務邏輯」寫進架構決策紀錄(ADR),並在程式碼審查中強制執行。若真的需要複雜的切換邏輯,應該把它實作成一個獨立的「路由決策服務」,而不是塞進代理層。
5.2 陷阱二:雙寫導致資料漂移
症狀:新舊兩邊的資料在幾週後開始出現無法解釋的差異,且數量逐漸累積。
根本原因:應用層雙寫沒有原子性保證,其中一邊失敗時若沒有補償,就會漂移。防禦方式是採用 Transaction Outbox 模式:把要發布的事件與本地交易寫入同一張 outbox 表,再由獨立的發布器讀取並送出。這保證了「本地交易成功」與「事件最終被發布」的一致性。
-- Outbox 表結構
CREATE TABLE outbox_event (
id BIGSERIAL PRIMARY KEY,
aggregate_type VARCHAR(64) NOT NULL,
aggregate_id VARCHAR(64) NOT NULL,
event_type VARCHAR(128) NOT NULL,
payload JSONB NOT NULL,
occurred_at TIMESTAMPTZ NOT NULL DEFAULT now(),
published_at TIMESTAMPTZ,
retry_count INT NOT NULL DEFAULT 0
);
CREATE INDEX idx_outbox_unpublished
ON outbox_event (id)
WHERE published_at IS NULL;
5.3 陷阱三:以技術模組而非業務能力切分
症狀:拆出來的是「使用者服務」、「訂單服務」、「產品服務」,每個服務都直接存取同一批共用資料表,服務之間還有大量同步呼叫。
這就是典型的「分散式單體」。它比原本的單體更糟,因為你同時承擔了單體的耦合與分散式系統的複雜度。
正確做法是回到領域驅動設計,以限界上下文(Bounded Context)為切分單位,並且確保每個上下文擁有自己的資料儲存。如果兩個能力共享同一張表,那它們很可能本來就屬於同一個上下文,不應該在此時被拆開。
5.4 陷阱四:絞殺順序由技術難度決定,而非業務價值
症狀:團隊從「最容易拆的」開始拆,兩年後發現拆掉的全是邊緣功能,核心的營收流程仍是鐵板一塊,而管理層已經對專案失去耐心。
防禦方式:建立一個「價值 × 可行性」的二維矩陣,並且每個季度至少要完成一次「高價值」象限的絞殺。這需要架構團隊與業務端共同排定順序,而不是由技術團隊單方面決定。
5.5 陷阱五:特性開關債
絞殺過程會累積大量特性開關。如果沒有清理機制,兩年後你會面對數百個開關,沒有人敢關掉任何一個,於是所有程式碼路徑都必須被測試與維護。
建議做法:每個開關建立時就指定預期的移除日期與負責人,並在 CI 中對超過期限的開關發出警告。開關的狀態也應該納入可觀測性,讓你知道目前有多少流量走在「非預期路徑」上。
六、2026 年的工具鏈與 AI 輔助實作
6.1 AI 輔助的三個實用場景
在絞殺專案中,AI 工具已經可以在三個環節產生實質效益:
但要強調:AI 產出的行為規格必須被驗證。錯的規格比沒有規格更危險,因為它會讓你在錯誤的基礎上建立信心。實務原則是「AI 提案、人類裁決、自動化驗證」。
6.2 平台工程必須提供的能力清單
若你正在規劃 2026 年的絞殺專案,請確認內部開發者平台已提供以下能力,否則專案進度會嚴重受制於基礎設施:
能力若缺乏時的後果
流量治理(金絲雀、權重路由)無法做分層放量,只能一次全切
Schema Registry事件格式變更導致消費者中斷
統一的身分與政策服務間呼叫各自實作授權,稽核無法通過
環境即程式碼無法快速建立影子比對所需的隔離環境
成本可視化並存期的雙倍成本無人掌握,預算失控
七、治理與度量:如何證明絞殺正在成功
7.1 四個必須持續追蹤的指標
7.2 組織層面的配套
技術架構的改變若沒有組織配套,註定失敗。三個必要條件是:
八、結語:絞殺是一場馬拉松,不是一次衝刺
Strangler Fig 模式之所以在 2026 年重回架構舞台的中心,不是因為它是什麼嶄新的技術,而是因為它終於在成本、工具與治理三個面向上同時具備了可執行性。它承認了一個現實:企業核心系統不可能被一次性取代,它只能被持續地、有紀律地掏空與重塑。
如果你正在規劃 2026 年的現代化專案,請記住三個最重要的原則。第一,第一個絞殺目標必須小、必須成功、必須完整走完全流程,它的價值在於驗證工具鏈與建立組織信心,不在於它本身的業務貢獻。第二,門面必須保持笨、資料必須單向流、交易邊界必須重新設計——這三件事做錯任何一件,專案都會退化成更糟的分散式單體。第三,絞殺的順序應該由業務價值驅動,而不是由技術難度驅動;否則你會發現自己花了兩年,卻只拆掉了無關緊要的邊緣功能。
最後,請接受一件事:絞殺過程中,新舊兩套系統並存是常態,不是失敗的徵兆。真正的失敗,是試圖跳過並存期、直接進行一次完美切換。榕樹不會在一夜之間長成,它只是在每一個交易日裡,安靜地多包覆一點宿主的枝幹。企業核心系統的微服務化,也是如此。