iT邦幫忙

2026 iThome 鐵人賽

DAY 14
0
Software Development

30 天把舊 ERP 整合成現代微服務平台:架構治理與工程實作雙軌實戰系列 第 14 篇

Day 14|Adapter Service 設計:別讓隔離層變成第二套 ERP

  • 分享至 

  • xImage
  •  

Adapter 建好之後,需求通常不會停:「這個欄位順手加一下」「那段流程也塞進來」。每一次看起來都是小事,但堆久了,這一層就會變得很難動。所以我想在一開始就講清楚,這一層該做什麼、不該做什麼。

本篇名詞小筆記

  • Facade:外觀介面,以用途清楚的操作包裝內部較複雜的介接細節。
  • DTO:資料傳輸物件(Data Transfer Object),明確定義服務輸入與輸出的欄位。
  • Retryable:可重試標記,表示錯誤可能是暫時性的;實際重試前仍要確認操作是否具備冪等性。
  • Contract Test:契約測試,用來驗證服務提供者與呼叫者對介面格式及行為的理解一致。Consumer-driven 指契約由呼叫端先寫,只列自己真正用到的欄位與行為,再交給提供者的建置流程去跑。

今天要解決的問題

「反正 Adapter 已經接到 ERP 了,這裡處理比較快。」

這個想法很容易理解。不過,如果訂單、庫存、報表與權限都往裡面加,之後任何需求都要改它,就會慢慢形成另一個很大的維護負擔。

所以,我會先留一份責任清單。每次加功能時,一起對照看看,這段工作是不是真的屬於 Adapter。

架構師視角:先定義責任清單,再逐案對照

把事情放回負責的人手上,後面的變更才比較好安排。所以我會先把 Adapter 的工作放在這幾項:協定轉換、ERP 認證、資料格式對應、timeout、錯誤標準化、稽核 metadata,以及必要的相容性處理。

跨域流程、前端需要的資料組裝、角色判斷與平台主資料,則回到各自的服務。

https://ithelp.ithome.com.tw/upload/images/20260925/20184230f36z7E5yzq.png

圖 Day 14-1:Adapter 責任邊界。

遇到不好分的邏輯,我會先問:如果以後 ERP 換掉了,這段判斷還需要嗎?如果還需要,就值得回到業務服務檢查;如果只是為了配合這套 ERP,才比較接近 Adapter 的介接工作。這個問題可以幫助討論,再搭配責任清單確認。

也可以偶爾回頭看看最近的 commit。Adapter 多半是因為 ERP 介面調整而改,還是每個新業務需求都會動到它?如果是後者,我會再看看有沒有功能放錯地方。

工程師視角:facade 介面與錯誤模型

萬用 execute() 雖然方便,但參數裡到底能要求哪些操作,就得再追下去看。介面寫得具體一點,審查權限、白名單與呼叫範圍時,也會比較有依據。

所以介面上,我會傾向把用途寫明白,例如 MaterialErpClient 與 OrderErpClient。每個操作有自己的 request/response DTO 與錯誤模型,看程式時比較知道它會做什麼,也比較容易測試:

// Adapter 對外的兩個 facade,依實作的 REST 端點整理;套件與 ERP 名稱已匿名化
public interface MaterialErpClient {
    MaterialDataResponse getMaterialData(@NotBlank String materialNo);
    List<MaterialListItemResponse> getAllMaterials(String keyword, String categoryCode, String unit);
    MaterialSyncResponse syncMaterials(@Valid MaterialSyncRequest request);
}

public interface OrderErpClient {
    ShipmentResponse createShipment(@Valid CreateShipmentRequest request); // 轉單,需來源出貨通知單號
    ReturnResponse createReturn(@Valid CreateReturnRequest request);       // 銷退單
}
// 呼叫端只拿得到這幾個操作;想「順便」做別的事,介面上沒有入口

錯誤模型則不分 facade,一律回同一種形狀:

{
  "success": false,
  "code": "ERP-TIMEOUT-001",
  "message": "ERP 未在時限內回應",
  "timestamp": "2026-09-10T08:30:00Z",
  "path": "/api/erp/orders/shipments",
  "traceId": "..."
}

錯誤回應也要守住範圍,ERP 原始 stack trace 與敏感欄位留在內部。即使回了 retryable,呼叫端仍要確認交易狀態與重試策略,尤其寫入操作,要先想好重送會不會重複執行。

策略取捨與限制

取捨 這樣選的理由 何時要重新評估
依領域建立多個 facade 每個操作用途明確,可審查、可測試 facade 數量成長到難以維護時,檢查是否邊界切錯
不提供萬用 execute 入口 避免呼叫端行為無法檢查 有明確受控的批次或維運需求時,需另訂控制
retryable 僅作為提示 Adapter 不掌握呼叫端交易狀態 特定操作已具備冪等保證時
業務規則一律不進 Adapter 保持變更來源單一 無;若確有例外,應以 ADR 記錄理由

DTO 要讓呼叫端看得懂,也要保留做決定需要的資訊。整理欄位時,就回到這個用途確認:哪些是平台需要的業務語意,哪些只是 ERP 的內部格式,分別處理。

驗證方式與衡量指標

Adapter 自己寫的測試,只能證明它照自己的定義做對了:就算欄位名稱改了,測試也跟著改,跑起來還是全部通過。所以契約改由呼叫端寫:訂單服務寫下它會送哪些欄位、期待拿回什麼,這份契約放進 Adapter 的建置流程;Adapter 改壞介面時,在自己的 CI 就知道弄壞了誰,不用等上線。這就是 consumer-driven contract test,用它驗證平台契約,再以 stub 模擬 timeout、SOAP fault、空值與格式異常:

驗證項目 做法 想確認什麼
契約相容性 consumer-driven contract test 介面變更是否破壞既有呼叫端?
異常路徑 stub 模擬 timeout、fault、空值、格式異常 錯誤是否都轉成標準模型?
敏感資訊外洩 檢查對外回應與 Log 內容 是否夾帶 ERP stack trace 或敏感欄位?
邊界守則 檢視 Adapter 近期變更來源 變更是否由 ERP 介面異動驅動?

最後一項可以放進定期架構審查。偶爾回頭看新增了哪些責任,發現放錯地方的功能,就提早整理,之後的修改也會比較輕鬆。

今天先整理到這裡

今天想留下的重點,是每次新增功能,都再回來看一次責任清單。把介接與業務工作分好,Adapter 才能維持原本的用途。下一篇,再往資料、錯誤與版本的轉換細節走。

參考資料

  1. W3C, W3C SOAP 1.2,查閱日期:2026-09-27。
  2. Spring, Spring Web Services,查閱日期:2026-09-27。

上一篇
Day 13|ERP SOAP 前為什麼需要 Adapter?
下一篇
Day 15|REST 與 SOAP 轉換:不只是 XML 改成 JSON
系列文
30 天把舊 ERP 整合成現代微服務平台:架構治理與工程實作雙軌實戰 共 18 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言