先確認要相容的是行為,不只是 WSDL
許多團隊一開始會把 SOAP 現代化理解成協定轉換:接收 XML、呼叫 REST,再回傳 XML。真正困難的部分通常不在序列化,而在既有消費端依賴的隱性行為。除了 WSDL 與 XSD,還要盤點 namespace、SOAPAction、欄位順序、可選元素、空值表示、日期格式、字元編碼、附件、逾時、重試與錯誤回應。部分客戶端甚至依賴特定 SOAP Fault 結構,或把某個非標準回應當成成功條件。
因此,第一步應建立契約清單,將每個 operation 的輸入、輸出、驗證規則、身分驗證方式與副作用寫清楚。不要只閱讀服務端程式碼;也要收集實際請求與回應樣本,確認正式流量中的 XML 是否真的符合文件。若文件與現況不同,遷移基準通常應以已被消費端依賴的實際行為為主,再決定哪些例外要保留、修正或逐步淘汰。
讓轉接層吸收舊契約,但不要複製舊架構
轉接層的責任是終止 SOAP 契約,完成驗證、認證、資料映射與錯誤轉譯,再呼叫新的應用服務。內部服務應使用適合新架構的模型與介面,而不是把 WSDL 產生的類別一路傳入核心領域。否則 namespace、nullable 規則與舊欄位命名會滲入新系統,未來移除 SOAP 時仍然無法真正脫離舊契約。
建議把外部契約模型、轉接映射與內部領域模型分開。若同一個 SOAP 欄位在不同 operation 中代表不同語意,應在轉接層明確拆分,不要建立一個過度通用的資料物件。共用 canonical model 只有在語意穩定、擁有清楚治理責任時才有價值;若只是為了減少類別數量,通常會產生大量條件判斷與難以追蹤的耦合。
- 薄型轉接層:適合舊契約與新服務語意接近的情況,維護成本低,但不能掩蓋關鍵差異。
- 反腐層:適合舊資料模型與新領域差異明顯的情況,映射較多,卻能保護新系統的邊界。
- 流程協調層:適合一個 SOAP operation 必須呼叫多個新服務的情況,但要明確處理部分成功、補償與重試。
轉接層也不應成為新的單體系統。商業規則應放在可測試的應用或領域服務中;轉接層只保留與協定、相容性及流程協調直接相關的邏輯。
把失敗語意、交易邊界與冪等性設計清楚
SOAP 呼叫常讓消費端期待同步完成,但現代化後的實作可能跨越資料庫、訊息佇列與多個服務。此時不能只把所有例外轉成一般 Fault。需要區分輸入錯誤、授權失敗、可重試的暫時故障、已接受但尚未完成的工作,以及可能已完成卻來不及回應的逾時。這些狀態若沒有穩定映射,舊客戶端重試時就可能造成重複扣帳、重複建單或重複通知。
對有副作用的 operation,應定義可重複識別的業務鍵或 idempotency key,並記錄請求處理狀態。若 SOAP 契約沒有專用欄位,可評估使用既有交易編號、WS-Addressing message ID,或由轉接層依穩定欄位建立識別,但雜湊不能取代業務上的唯一性判斷。跨服務流程則應選擇明確的補償或最終一致性策略,不要假裝分散式呼叫仍具有單一資料庫交易的原子性。
- 逾時:區分「尚未執行」與「執行結果未知」,並提供可查詢狀態的方式。
- 重試:只重試可安全重放的錯誤,採用退避機制,避免轉接層與客戶端同時無限重試。
- 錯誤轉譯:保留既有 Fault code 與必要 detail,同時在內部使用可觀測、可分類的錯誤碼。
安全相容也要單獨處理。若既有服務使用 WS-Security、簽章或憑證,轉接層必須先驗證訊息,再轉換成內部身分與授權資訊;不能因後端改用 OAuth 或服務帳號,就直接信任從 XML 讀出的使用者欄位。
以契約測試與可逆切換完成遷移
驗證不能只靠幾組手動 XML。應從正式樣本整理契約測試,涵蓋最小輸入、完整輸入、空值、未知元素、邊界日期、重複請求、簽章失敗與各類 Fault。測試不只比較資料值,也要檢查 namespace、元素是否省略、錯誤碼、HTTP 狀態與回應標頭。對無法完全結構化比較的欄位,例如動態時間或追蹤編號,可先正規化再比對。
切換時可讓舊服務與新轉接層並行,先使用鏡像流量或錄製後重播進行差異比較。具有副作用的請求不可直接雙寫;可在新路徑停用實際提交,或只比較轉換與決策結果。正式導流應依 operation、消費端或可控比例逐步進行,並保留快速回切能力。
- 觀測:以 correlation ID 串起 SOAP 請求、轉接映射、內部呼叫與最終回應。
- 版本:把相容修正與破壞性變更分開;需要新語意時,建立明確的新契約或端點。
- 退場:依實際流量確認消費端已遷移,再停止舊 operation,不以文件上的名單作為唯一依據。
成功的現代化不是讓 SOAP 立即消失,而是讓它變成受控的邊界。當契約、映射、錯誤與切換策略都可測試、可觀測、可回復,團隊才能逐步替換內部實作,而不必要求所有外部系統在同一天升級。