先把流程視為狀態機,而不是一串 API
典型流程可能依序建立表單、寫入 ERP、送出簽核、更新 CRM,再透過 LINE 或電子郵件通知。若只以同步程式逐步呼叫,任何一段逾時都會留下模糊狀態:請求可能根本沒有到達,也可能已成功但回應遺失。此時直接重跑整段流程,容易建立重複單據、送出兩次簽核,或讓同一位主管收到多則通知。
較可靠的做法是為每筆業務流程建立獨立識別碼,並持久化目前狀態、每個步驟的嘗試次數、外部系統識別碼與最後錯誤。狀態應描述可驗證的事實,例如「ERP 單據已建立」或「簽核建立結果待確認」,而不是只有「處理中」。工作程序即使重新啟動,也能從已確認的節點繼續,而非從頭猜測。
先區分重試、補償與人工介入
不是所有錯誤都應自動重試,也不是所有成功動作都能真正回滾。工程團隊應逐步標記副作用、可逆性、查詢能力與業務風險,再選擇處理方式。
- 短暫性錯誤:網路中斷、服務限流或暫時不可用,可採指數退避與次數上限;重試前必須確認操作具備冪等性。
- 明確拒絕:欄位驗證失敗、權限不足或簽核規則缺失,重試通常沒有意義,應停止流程並提供可修正的錯誤內容。
- 結果不明:呼叫逾時但外部系統可能已完成,應先以業務鍵或請求識別碼查詢結果,不能直接再建立一次。
- 可逆副作用:尚未使用的草稿或暫存資料可透過取消、作廢或刪除 API 補償,但須保留原始稽核紀錄。
- 不可逆副作用:已核准決策、已讀通知或已產生的會計影響,不應假裝能消失;應建立更正、撤銷或反向交易並通知責任人。
三個常用模式必須搭配使用
冪等鍵適合保護建立表單、送簽與發送通知。相同業務事件無論重送幾次,接收端都應回傳同一結果;若外部 API 不支援冪等鍵,整合層可保存業務鍵與外部識別碼的對照,並在建立前後查詢。鍵值需要綁定操作與版本,避免「同一申請的重新送簽」被誤判成重複請求。
交易寄件匣可避免資料庫已更新但事件未送出的雙寫問題:業務資料與待發事件在同一個本地交易中提交,再由背景工作者發布。跨系統流程則可採 Saga,為每個已完成步驟定義對應補償,例如 ERP 建單後簽核建立失敗,可將單據標成待處理或作廢,而不是直接刪除。補償本身也會失敗,因此同樣需要冪等、重試上限與獨立狀態。
簽核與通知有不同的補償語意
簽核牽涉人的決策與稽核軌跡。流程已送出但後續同步失敗時,較安全的處理通常是暫停後續動作、標示異常並保留簽核歷程。若表單內容在簽核途中變更,應建立新版本並明確要求重新簽核,不要靜默覆寫主管當時看到的內容。取消簽核也必須記錄原因、操作者與被取代的版本。
通知則通常無法收回。補償重點是防止重複、避免過早宣告成功,並在必要時發送清楚的更正訊息。通知事件應包含收件者、範本版本、業務事件與通道,讓系統能判斷同一通知是否已送達。若 LINE 發送成功但電子郵件失敗,應只重試失敗通道,不應重新廣播全部通知。
讓失敗可以被操作,而不只是被記錄
一套可維運的流程至少要呈現業務識別碼、目前步驟、外部系統識別碼、錯誤分類、下次重試時間及補償狀態。告警應聚焦在需要人處理的狀態,例如重試耗盡、資料矛盾或補償失敗,而不是每次短暫逾時。人工操作介面則應提供「重試此步驟」、「確認外部結果」、「執行補償」與「結案並註記」等受權限控制的動作。
上線前,工程團隊應刻意測試每個邊界:在外部系統成功後切斷回應、於事件發布前重啟工作者、讓補償 API 失敗,以及重送同一事件。若系統能從持久化狀態恢復,且操作人員能解釋每筆資料為何停在目前位置,補償設計才算完整。流程跨越多個舊系統或第三方服務時,由整合團隊共同定義狀態與責任邊界,通常比事後追加例外處理更穩妥。
