🎯 什麼情境該想到我
當你「同一種訊息卻由不同來源以各自不同的格式送來,你想先把它們統一成一個共通格式再處理」的時候。
⚙️ 怎麼用(步驟 / 公式)
意圖:把語意相同但格式不同的多種訊息,各自轉換成一個共同的標準格式。
做法 / 結構要點:
- 用類型判斷(如訊息型別、來源)辨識進來的訊息屬於哪種格式。
- 為每種來源格式準備一個對應的轉換器。
- 透過路由把訊息導到對應轉換器,輸出統一格式。
★ 結構:路由器 + N 個轉換器 + 一個共同輸出
供應商A 的 XML ──┐
供應商B 的 CSV ──┼──[[訊息路由器]]──▶ 各自的 [[訊息轉換器]] ──▶ 統一格式 ──▶ 下游
供應商C 的 JSON ─┘ ↑ 依來源/格式分派 ↑ 每種來源一個,互不影響
★ 這個模式的價值在於「隔離」:新增一個供應商 = 加一個轉換器 + 一條路由規則,
既有的 N 個轉換器一行都不用改。→ 這就是 工具-開閉原則 在整合層的體現。
★ Go 的實作:分派表 + 純函式轉換器
type Normalizer func([]byte) (CanonicalOrder, error)
var normalizers = map[string]Normalizer{
"supplier-a": normalizeSupplierA, // ★ 每個都是純函式 → 可以用表格測試窮舉
"supplier-b": normalizeSupplierB,
}
func normalize(source string, raw []byte) (CanonicalOrder, error) {
n, ok := normalizers[source]
if !ok {
// ★★ 未知來源絕對不要靜默丟棄
return CanonicalOrder{}, fmt.Errorf("%w: 未知來源 %q", ErrInvalidMessage, source)
}
return n(raw)
}★★ 「怎麼知道這是哪個來源」有三種方式,可靠度差很多:
① ★ 通道/佇列決定(每個供應商一條專用通道)—— ★ 最可靠,推薦
② ★ 標頭欄位(source: "supplier-a")—— 可靠,但要防偽造
③ ★★ 猜格式(sniffing:開頭是 < 就是 XML)—— ★ 最脆弱,但整合遺留系統時常常只能這樣
★ ① 優先。它讓「這是誰送來的」變成基礎設施保證,而不是資料內容的解讀。
★★ 這個模式最大的風險:共同格式會被「最後一個供應商」綁架
★ 典型的腐敗過程:
第 1 個供應商 → CanonicalOrder 有 8 個欄位,乾淨
第 5 個供應商 → 它有一個特殊的「分期付款期數」→ 加一個欄位(其他 4 個都是 null)
第 12 個供應商 → 加三個欄位
→ ★★ CanonicalOrder 變成所有供應商欄位的聯集,而每個實例只有 20% 的欄位有值
→ ★ 下游必須判斷每個欄位「這個來源有沒有給」⇒ 正規化的價值消失了
★★ 對策:共同格式只包含「所有來源都有、且下游都需要」的欄位。
來源特有的資料放一個明確的SourceSpecific json.RawMessage欄位,
並且下游原則上不該讀它——要讀就表示那個下游其實需要知道來源,
那就別走正規化這條路。★ 這是 標準資料模型 的同一個陷阱,規模小一號但一樣致命。
★★ 資訊損失是必然的,要明確決定丟什麼
type CanonicalOrder struct {
OrderID string
Lines []Line
Total Money
// ★ 明確記錄來源與原始資料位置,讓「需要時能回去查」
Source string `json:"source"`
RawRef string `json:"raw_ref"` // ★★ 原始訊息的儲存位置
Dropped []string `json:"dropped,omitempty"` // ★ 明確記錄丟了哪些欄位
}★★ RawRef 是最有價值的一欄:把原始訊息落地保存(訊息存放區/寄物單),
正規化後的訊息只帶位址。
→ 爭議發生時(「我明明有送這個欄位」)你能拿出原始資料,這在 B2B 整合裡是必需的。
★ 兄弟辨析
| 差別 | |
|---|---|
| 訊息轉換器 | ★ 單一轉換;正規化器是「路由 + N 個轉換」的組合 |
| 標準資料模型 | ★ 正規化器的輸出格式如果推廣到整個企業,就變成它——★ 注意那一頁的代價 |
| 格式指示器 | 判斷來源/版本的依據之一 |
| 防腐層 | ★ 同一想法在 DDD 的說法(隔離外部模型) |
| 配接器模式 | 行程內的同一想法 |
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
-
每多一種來源格式,就要多一個轉換器。
-
常與標準資料模型搭配:轉成的「統一格式」即中立模型。
-
辨識格式的規則要可靠,誤判會轉錯。
-
★★ 共同格式會被「最後一個供應商」綁架,變成所有來源欄位的聯集。只放「所有來源都有且下游都需要」的欄位。
-
★★ 來源特有資料放明確的
SourceSpecific,而下游原則上不該讀它。要讀就表示它其實需要知道來源。 -
★★ 要保存原始訊息並在正規化結果裡帶
RawRef。B2B 整合的爭議處理需要它。 -
★ 「這是哪個來源」優先用通道決定,不要靠猜格式或信任資料裡的欄位。
-
★★ 未知來源絕對不要靜默丟棄,送 無效訊息通道 並計數。
-
★ 資訊損失是必然的。要明確記錄丟了哪些欄位,而不是悄悄丟。
-
每個轉換器要是純函式,才能用表格測試覆蓋每個來源的邊界。
-
這是 標準資料模型 的小一號版本,同樣的陷阱——先讀那一頁。