第 10 / 13 部 待錄

限界上下文與上下文地圖

《Domain-Driven Design 領域驅動設計》Podcast 準備稿:限界上下文與上下文地圖

書名: 領域驅動設計 Domain-Driven Design(Tackling Complexity in the Heart of Software) 作者: Eric Evans 系列: 啃一本大書(恩普拉氏) 公開標題建議: 啃一本大書|Domain-Driven Design.Bounded Context:微服務切分的理論源頭 (10/13) 涵蓋範圍: Part IV 第14章 保持模型完整(Maintaining Model Integrity)

背景速覽

來了。這集是整本書、整季最重要的一集。Part IV「策略設計」開場第14章,講的是 Bounded Context(限界上下文)Context Map(上下文地圖)——DDD 兩大支柱的第二根(第一根是第2集的統一語言)。如果說前九集都在教「怎麼把一個模型做好」,這集回答的是一個更大的問題:當系統大到一個統一模型裝不下時,怎麼辦? 而它的答案,正是十幾年後微服務切分的理論基礎。這集務必講透、講重。

一句話重點

別妄想用單一模型統一整個大系統——而是明確劃出 Bounded Context(每個模型成立的邊界、邊界內語言嚴格一致),用 Context Map 畫出所有上下文及其關係,並為每對關係選一種整合策略(共享核心、客戶/供應商、防腐層、各行其道……)。

值得討論的重點

1. 為什麼需要多重模型

試圖用單一模型統一所有軟體,風險很大:同時替換太多 legacy 會失敗、大型專案協調開銷高到停滯、特殊需求的應用被迫用不合適的模型、討好所有人的模型複雜到沒人會用。而且模型分歧不只來自技術——還來自政治分裂、管理優先序差異、團隊組織。所以我們需要的不是「消滅分歧」,而是標記出不同模型的邊界和關係,有意識地選策略、一致地執行。

2. Bounded Context:模型成立的邊界

定義要記熟:Bounded Context 是模型適用範圍的明確邊界。邊界內單一模型嚴格一致;邊界外,其他模型適用,術語、概念、規則都可能不同。 用團隊組織、應用的特定部分、程式碼庫、資料庫 schema 等物理表現來設邊界。跨邊界整合必然涉及某種翻譯。

關鍵澄清:Bounded Context 不是 Module。 Module 組織同一個模型內的元素;Bounded Context 標示不同模型之間的分界。定義 Bounded Context 的收益:邊界內團隊得到清晰、邊界外團隊得到自由

模型崩解的兩個徵兆:Duplicate concepts(兩個元素其實是同一概念,每次都要改兩處、最終分歧)、False cognates(兩人用同一個詞卻指不同東西——像西班牙語 “embarazada” 不是 “embarrassed” 而是「懷孕」)。

3. Context Map:畫出全局

Context Map 是「專案管理與軟體設計的交集」——畫出專案中所有 Bounded Context 及其關係。實踐:辨識每個運作中的模型並定義其邊界、命名每個 Context(名字進統一語言)、描述模型間的接觸點和翻譯機制、先描繪現有地形、再談轉換。Evans 的金句:別說「George 團隊的東西改了所以我們也要改」,要說「Transport Network 模型改了,所以我們要更新 Booking context 的 translator」——用 Context 的名字溝通,不是用人名。

4. 上下文間的關係模式(這是精華中的精華)

從高度合作到完全獨立的一系列策略:

  • Shared Kernel(共享核心):兩團隊同意共享的模型子集,動它要先諮詢對方。
  • Customer/Supplier(客戶/供應商):上游餵養下游,下游當「客戶」在規劃會議上協商需求,用自動化驗收測試保護介面。
  • Conformist(追隨者):上游不甩你、但其設計品質尚可——下游完全遵從上游模型,消除翻譯複雜度(用現成元件常常就該如此)。
  • Anticorruption Layer(防腐層):上游設計爛、或是 legacy——建一個隔離層,用你自己的領域術語提供功能,內部由 Facade + Adapter + Translator 雙向翻譯。這是最重要的整合模式之一。
  • Separate Ways(各行其道):沒有不可或缺的關係就完全切斷,讓各自找簡單專門的解。
  • Open Host Service + Published Language:要跟很多系統整合時,開放一套協定(如 CML 化學標記語言)當通用交換格式。

注意事項

⚠️ 這集是 DDD 在微服務時代重新爆紅的真正原因,務必講重。 為什麼 2003 的書會在 2015 後紅起來?因為微服務逼著每個人回答「服務該怎麼切」,而 Bounded Context 給了答案:一個 Bounded Context 往往就對應一個微服務的邊界。 不懂 Bounded Context 就切微服務,會切出一堆共享資料庫、語義打架、改一個要動五個的「分散式單體」。這條是整季最值得反覆敲的洞見。

⚠️ 這正是「只學戰術、漏掉戰略」的 DDD 誤讀最致命的地方。 很多團隊學了 Entity/Repository 就以為會 DDD 了,結果在一個巨大的單一模型裡套戰術模式,照樣一團亂。真正讓 DDD 偉大的是這集——先用 Bounded Context 把系統切對,戰術模式才有意義。如果整季只能讓聽眾記住一集,就是這集。

⚠️ 防腐層的成本要平衡地講。Evans 用長城的故事警告:隔離的好處必須和成本相平衡——長城耗盡了至少一個朝代的國力。任何整合都有開銷,從持續整合、共享核心、客戶/供應商,到追隨者、防腐層皆然。別為了「乾淨」而到處蓋防腐層,整合需求廣泛時,有時讓自己的模型靠近外部系統反而更省。

⚠️ Continuous Integration 在 DDD 裡有特殊含義:它只在單一 Bounded Context 內是必要的。跨上下文的設計問題(含翻譯)不需要同樣的節奏。別把「所有東西都持續整合到同一個大模型」當美德——那正是 Bounded Context 要打破的。

專家補充

💡 「一個團隊對應一個 Bounded Context」這條原則,後來在《Team Topologies》和「逆康威定律」(Inverse Conway Maneuver)裡被放大。康威定律說「系統架構會長得像組織溝通結構」,DDD 的 Context Map 等於是在有意識地設計組織邊界 = 模型邊界 = 服務邊界。微服務團隊組織、平台工程的「stream-aligned team」都是這條線的延伸。這是 DDD 從「軟體設計」溢出到「組織設計」的關鍵。

💡 Context Mapping 後來被社群進一步發展。原書的關係模式被整理成更完整的「Context Map patterns」(加上 Partnership、Big Ball of Mud 等),還有人做成可視覺化的工具。想實際畫 Context Map 的聽眾,可以搜 “Context Mapping cheat sheet”(社群版的有九種關係)。這是策略 DDD 落地最實用的一張圖。

💡 防腐層(Anticorruption Layer, ACL)是現代系統最常活用的 DDD 概念之一。任何時候你要接一個爛掉的 legacy、一個第三方 API、一個你控制不了的外部系統——在中間蓋一層 ACL,用你自己的語言重新抽象它,你的核心模型就不會被對方的混亂污染。這是「明天就能用」的高價值模式。Strangler Fig(絞殺者)模式淘汰 legacy,骨子裡也是靠 ACL。

討論問題

🎙️ 開場鉤子:「如果你只聽這一季的一集,聽這集。Bounded Context——限界上下文——是 DDD 真正的王冠。它回答了一個讓無數團隊翻車的問題:當系統大到一個模型裝不下,你該在哪裡畫線?而這條線,正是十幾年後微服務切分的理論源頭。」

🎙️ 自問自答:「為什麼不能用一個統一模型管整個大系統?」——因為討好所有人的模型會複雜到沒人會用,而且模型分歧不只來自技術,還來自政治、組織、優先序。與其對抗分歧,不如明確劃界、管理關係。

🎙️ 帶走的一題:「想想你正在切(或想切)的微服務——你是按『技術層』切(一個 service 管 user、一個管 order)還是按『業務上下文』切?如果同一個『商品』概念在好幾個服務裡語義都不一樣卻共用一張表,那你很可能切錯了。」

更大範圍關聯

  • DDD 的第二支柱、戰略設計的核心:統一語言(EP2)負責「在邊界內講同一種話」,Bounded Context(本集)負責「劃出這種話成立的邊界」。兩者合起來就是 Evans 戰略思想的全部地基。
  • 微服務切分的理論基礎:這是 DDD 在微服務時代爆紅的直接原因。Sam Newman《Building Microservices》明確以 Bounded Context 作為服務切分的首要準則。不懂 Bounded Context 切微服務 = 切出分散式單體。
  • 連到康威定律 / Team Topologies:「一團隊一上下文」把模型邊界、服務邊界、組織邊界對齊,是逆康威定律和現代團隊拓撲的思想源頭。DDD 在這裡從軟體設計溢出到組織設計。
  • 連到 Clean Architecture:防腐層(ACL)和 Clean Architecture 的「邊界 + 依賴反轉」是同一種防禦思想——把你控制不了的東西隔離在翻譯層之外,保護核心。Strangler Fig 淘汰 legacy 也靠它。
  • 承接 Aggregate:第5集的「Aggregate = 一致性邊界」在這集放大成「Bounded Context = 模型/服務邊界」。從物件層的邊界,到系統層的邊界,是同一種「畫對邊界」的智慧。

錄製建議

  • 建議時長:約 28-30 分鐘(最重要的一集,值得最長)。配比:為什麼需要多重模型約 4 分、Bounded Context 約 8 分(定義 + 不是 Module + 兩個崩解徵兆)、Context Map 約 5 分、六種關係模式約 9 分(重點講 Shared Kernel、Conformist、Anticorruption Layer、Separate Ways)、微服務連結約 4 分。
  • 討論策略:這是整季的高潮,開場就要把份量點明——「如果只聽一集,聽這集」。核心敘事線:「單一模型裝不下 → 劃 Bounded Context → 畫 Context Map → 為每對關係選策略」。六種關係模式別平均用力,重點放在 Conformist(現成元件就該追隨)、Anticorruption Layer(接 legacy 的防禦)、Separate Ways(別過度整合)這幾個最常用的。微服務連結放在後段當「為什麼這集這麼重要」的收束,反覆敲「Bounded Context = 服務邊界」。
  • 全程扣住整季定錨:這才是 DDD 的精華,不是 Entity/Repository。 講完這集,聽眾應該徹底明白為什麼說「只學戰術是學了 DDD 的皮」。