🎯 什麼情境該想到我

當你「下游依賴一個不會為你做任何調整的上游,而你也沒有本錢建翻譯層」的時候。你決定乾脆完全順從上游的模型,用放棄自主換取大幅降低整合成本。

⚙️ 怎麼用(步驟 / 公式)

意圖:下游完全遵循(conform to)上游的模型,不做任何轉換。直接沿用上游的通用語言與模型,以此消除翻譯成本,代價是失去自己塑模的自由。

做法要點:

  1. 確認上游模型品質尚可、且大致符合你的需要——否則別追隨。
  2. 在下游直接採用上游的模型與語彙,不另建自己的領域模型或翻譯層。
  3. 跟著上游的模型演進走,把整合維護成本壓到最低。
  4. 定期重評:若上游模型開始拖累你的核心領域,改建防腐層取回自主。

★★ 它是一個「誠實的選擇」,不是一個失敗

★ 追隨者(Conformist)= ★★ 你放棄自己的模型,直接採用上游的
→ ★ 三個前提同時成立時,它是「正確的選擇」:
  ① ★★ 上游不會為你改(★ 第三方、跨組織、或組織上你沒有話語權)
  ② ★★ 上游的模型「夠接近」你要的(★ 不會讓你的領域邏輯變扭曲)
  ③ ★ 翻譯的成本大於「模型被污染」的成本

★★ 和 防腐層 的差別只有一個:★ 你要不要付翻譯的錢。
★ 追隨者是省下翻譯成本,接受模型被上游形塑。
而這常常是對的——★★ 特別是當那個上下文本來就是 支撐子領域 或 通用子領域。

★★ 什麼時候追隨者是明確正確的

① ★★ 上游是一個成熟的產業標準
   ★ 用 Stripe 的 Charge / Refund 模型做金流、用 OIDC 的 claims 做身分
   → ★ 自己發明一套「更好的」模型,★★ 只會讓你和整個生態脫節

② ★★ 那個上下文是通用子領域
   ★ 「寄信」的模型就用寄信服務的模型(★ Message、Recipient、Template)
   → ★ 你的領域沒有「更好的寄信模型」要表達

③ ★ 資料是「唯讀的參照」
   ★ 你只是讀一份匯率表、地址資料庫、商品目錄
   → ★★ 沒有領域邏輯要保護,翻譯是純成本

④ ★ 短期/一次性的整合
   → ★ 一個資料遷移腳本不需要防腐層

★★ 判準:這個上下文裡有「你獨特的業務規則」嗎?
沒有 → ★ 追隨者。
有 → ★★ 防腐層(★ 那些規則需要一個不被上游污染的模型來承載)。

★★ 它的具體代價:上游的模型會滲透多遠

★ 追隨者的滲透路徑(★ 這是它唯一真正的風險):
  ★ 第 1 步:★ 你的 repo 回傳 stripe.Charge
  ★ 第 2 步:★ 你的 service 接受 stripe.Charge 參數
  ★★ 第 3 步:★ 你的領域邏輯裡出現 if charge.Status == "succeeded"
  ★★ 第 4 步:★ Stripe 改版/你要換供應商 ⇒ ★ 三十個檔案要改

★★ 控制滲透的兩個低成本手段(★ 不是完整的防腐層):

① ★★ 把上游型別限制在一個 package 內
   ★ grep -rn "stripe" internal/domain/ → ★★ 應該是空的
   → ★ 這是 depguard 一行設定的事(見 [[分離介面]])

② ★ 只轉換「你的領域邏輯真正會判斷的那幾個欄位」
   ★ if charge.Status == "succeeded" → ★★ if payment.IsCaptured()
   → ★ 一個十行的轉換,★★ 而不是一整套領域模型

★★ ② 是追隨者與防腐層之間的中間點,而它常常是最划算的:
★ 「大部分照抄,但把領域邏輯會判斷的欄位翻譯掉」。

★★ 追隨者最危險的情況:上游的模型「幾乎」夠用

★ 危險的樣貌:
  ★ 上游的 Customer 有 90% 是你要的
  → ★ 於是你追隨它,★★ 但那 10% 的差異用「額外的欄位」與「特殊處理」補上
  → ★★ 於是你有一個「上游模型 + 十個補丁」的東西
  → ★ 而每次上游改版,你的補丁要重新驗證一次

★★ 判準:你需要在上游模型上「補」東西嗎?
不需要 → ★ 追隨者很乾淨。
需要,而且補的東西越來越多 → ★★ 那是「該升級成防腐層」的訊號——
★ 因為你其實有一個自己的模型,只是它寄生在上游的模型上。

★ 從追隨者升級成防腐層的時機

★ 三個具體的觸發點:
① ★★ 你開始要「換掉上游」(★ 換金流商、換身分供應商)
   → ★ 那時防腐層的價值一次兌現
② ★★ 上游的破壞性變更讓你改了三十個檔案
   → ★ 那是「滲透太深」的實證
③ ★ 那個上下文從支撐升級成核心(見 [[核心子領域]])
   → ★★ 核心子領域不該用別人的模型

★ 兄弟辨析

差別
防腐層★★ 付翻譯成本,換模型自主
客戶-供應商★ 上游有配合義務
共享核心★ 雙方共用,不是單方採用
各行其道★ 不整合

🧪 我實際套用的紀錄

  • (待填)

⚠️ 注意 / 什麼時候不適用

  • 追隨代表放棄下游模型的自主與純淨;若這塊是你的核心領域,追隨會侵蝕它,應改用防腐層。

  • 只適合上游模型還算合理、能接受的情況;上游模型很糟時追隨等於把爛模型吸進來。

  • 上游一改,下游被動跟著改,缺乏緩衝。

  • ★★ 追隨者是誠實的選擇,不是失敗。 判準:這個上下文裡有「你獨特的業務規則」嗎?沒有 → 追隨者。

  • ★★ 和 防腐層 的差別只有一個:你要不要付翻譯的錢。

  • ★★ 上游是產業標準時(Stripe、OIDC),自己發明「更好的」模型只會讓你和生態脫節。

  • ★★ 唯一真正的風險是滲透深度。 grep -rn "<供應商>" internal/domain/ 應該是空的——用 depguard 一行設定守住。

  • ★★ 最划算的中間點:大部分照抄,但把「領域邏輯會判斷的欄位」翻譯掉(charge.Status == "succeeded" → payment.IsCaptured())。

  • ★★ 最危險的情況是上游模型「幾乎」夠用:於是你有「上游模型 + 十個補丁」,而每次改版補丁都要重新驗證。

  • ★ 判準:你需要在上游模型上「補」東西嗎? 補的越來越多 → 該升級成防腐層。

  • ★ 升級的三個觸發點:要換掉上游、破壞性變更改了三十個檔案、那個上下文變成核心子領域。

🔗 相關工具