🎯 什麼情境該想到我
當你「下游依賴一個不會為你做任何調整的上游,而你也沒有本錢建翻譯層」的時候。你決定乾脆完全順從上游的模型,用放棄自主換取大幅降低整合成本。
⚙️ 怎麼用(步驟 / 公式)
意圖:下游完全遵循(conform to)上游的模型,不做任何轉換。直接沿用上游的通用語言與模型,以此消除翻譯成本,代價是失去自己塑模的自由。
做法要點:
- 確認上游模型品質尚可、且大致符合你的需要——否則別追隨。
- 在下游直接採用上游的模型與語彙,不另建自己的領域模型或翻譯層。
- 跟著上游的模型演進走,把整合維護成本壓到最低。
- 定期重評:若上游模型開始拖累你的核心領域,改建防腐層取回自主。
★★ 它是一個「誠實的選擇」,不是一個失敗
★ 追隨者(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())。 -
★★ 最危險的情況是上游模型「幾乎」夠用:於是你有「上游模型 + 十個補丁」,而每次改版補丁都要重新驗證。
-
★ 判準:你需要在上游模型上「補」東西嗎? 補的越來越多 → 該升級成防腐層。
-
★ 升級的三個觸發點:要換掉上游、破壞性變更改了三十個檔案、那個上下文變成核心子領域。
🔗 相關工具
- 防腐層 Anticorruption Layer(不願追隨、要保護自身模型時的相反選擇)
- 客戶-供應商 Customer-Supplier(上游願意配合時的更佳關係)
- 各行其道 Separate Ways(若整合根本不划算,乾脆不整合)
- 領域驅動設計
- 防腐層 —— ★ 付翻譯成本換模型自主
- 客戶-供應商 —— 上游有配合義務
- 共享核心 —— 雙方共用
- 各行其道 —— 不整合
- 通用子領域、支撐子領域 —— 適合追隨的地方
- 核心子領域 —— 不該用別人的模型
- 分離介面 —— 用 depguard 控制滲透