當一個系統長到夠大,它最嚴重的問題通常不是技術問題。

需求討論裡,PM 說的「訂單」、財務說的「訂單」、物流說的「訂單」,指的是三種不同的東西。寫 code 的工程師把這三種東西全部塞進同一個 Order 類別,每次加一個欄位都要確認不會破壞其他兩個部門的邏輯。這個問題不用等到 100 個工程師,30 個工程師、五個業務部門,這個問題就會出現。

DDD 戰略設計的起點,是承認這個現象是正常的——不同部門看同一個業務概念,本來就有不同的視角和邏輯。問題是你要怎麼在軟體系統裡,把這些視角明確地切割開來,而不是讓它們互相污染。


Ubiquitous Language:讓程式碼說業務的語言

DDD 的第一個概念是 Ubiquitous Language(通用語言)。

這個詞容易被誤解為「和 PM 好好溝通」這種軟性建議。它的實際意思更具體:在一個邊界內,技術術語和業務術語必須一一對應,不能有分歧。

如果業務說「訂單確認」,對應的 code 就應該有 OrderConfirmed 事件或 confirm() 方法,不是 updateStatus('CONFIRMED')OrderStatusChanged。如果業務說「退款申請」和「退款執行」是兩件不同的事,code 裡就必須有兩個不同的概念,不能用同一個 Refund class 的不同 status 混合表達。

Ubiquitous Language 的副作用是:當業務和技術的詞彙不一樣,你就知道邊界切錯了,或者 code 跟業務邏輯脫節了。這個不一致本身就是信號。


Bounded Context:「這個詞在這個邊界裡只有一種意思」

Ubiquitous Language 有一個前提:它只在一個 Bounded Context 裡有效。

Bounded Context 是一個邊界,在這個邊界內,每個業務概念有且只有一種定義。

電商系統的「訂單」在三個 Bounded Context 裡是三種東西:

  • 銷售 BC:訂單是「用戶的購買意向」,核心欄位是商品清單、價格、折扣、促銷碼
  • 物流 BC:訂單是「一批貨的配送任務」,核心欄位是收件地址、物流商、包裹重量、配送時程
  • 財務 BC:訂單是「一筆應收帳款的憑證」,核心欄位是金額、發票資訊、稅率、收款狀態

這三個「訂單」共用同一個 order_id,但它們的 schema、生命週期、業務規則完全不同。Bounded Context 的工作,是讓這三種 Order class 在各自的邊界內獨立演進,而不是在一個 Order 裡加 50 個欄位試圖同時服務三個部門。


Context Map:邊界之間的關係圖

切了多個 Bounded Context,你還需要知道它們怎麼互動——這是 Context Map 的工作。

Context Map 描述的不是系統架構圖(哪個服務呼叫哪個),而是邊界之間的語意關係和權力關係。幾個常見的關係類型:

Shared Kernel:兩個 BC 共用一個小型的 domain model。改動這個 Kernel 要兩邊團隊都同意。適合高度相關、變化頻率接近的 BC,代價是失去了獨立演進的彈性。

Customer-Supplier:一個 BC(Supplier)為另一個(Customer)提供 API。Supplier 有主導權,Customer 配合 Supplier 的 schema。適合明確的上下游關係——物流 BC 提供配送狀態 API,銷售 BC 使用它,但不影響它的設計。

Conformist:一個 BC 完全依賴另一個的 model,不加任何轉換直接採用。常見於使用外部系統(如金流 API)——你只能照著它的規範走。

Anti-Corruption Layer(ACL):兩個 BC 之間加一層翻譯。當外部 BC 的 domain model 和你的不相容(或者你不信任它的設計),ACL 在邊界處把「外面的概念」翻譯成「你自己的概念」。這是讓 BC 獨立演進的防火牆。


DDD 戰略設計和微服務的關係

DDD 的 Bounded Context 和微服務邊界高度相關,但不是同一件事。

Bounded Context 是業務概念邊界,微服務是部署和運維邊界。 一個 Bounded Context 可以對應一個微服務,也可以先放在 monolith 裡、等規模確認後再拆。反過來,一個 Bounded Context 有時候因為效能或組織原因拆成多個服務,但它們在業務語意上仍然屬於同一個邊界。

「拆微服務」的正確起點是先確認 Bounded Context,不是先決定「每個功能都要一個服務」。按 Bounded Context 拆服務有一個重要性質:BC 之間的介面通常比 BC 內部的介面更穩定。物流 BC 對銷售 BC 提供的 API 變化頻率低,但物流 BC 內部的配送排程邏輯可能每季都在改——這讓 BC 邊界成為比較合理的服務邊界。


戰略設計最常見的兩種誤解

誤解一:DDD = 微服務。DDD 的戰略設計是幫你找到合理的邊界,和你最後部署成一個 monolith 還是十個微服務無關。很多系統用 DDD 把業務邏輯組織得很清楚,但整個系統仍然跑在同一個 process 裡——這沒有任何問題,甚至是很多早期系統的正確選擇。

誤解二:先畫 Context Map 再寫 code。Bounded Context 不是設計時期一次畫好就固定的。業務會演進,你對 domain 的理解會加深——邊界會移動,Ubiquitous Language 會被修正。DDD 戰略設計是一個持續進行的對話,不是一次性的架構設計作業。


什麼時候 DDD 戰略設計值得投入

DDD 戰略設計的成本是真實的:它要求開發和業務長期對話、維護多個 Bounded Context 的 codebase、設計 Context Map 上的整合介面。這個成本在小系統根本不值得。

值得投入的場景:業務複雜度是真實的——多個部門各自有不同業務邏輯的同一個概念、需求文件裡同一個詞在不同地方意思不一樣、你在維護一個超過 5 年的大型系統。這些情況下,DDD 戰略設計的框架讓複雜度可以被管理。

如果你的「業務邏輯」是 CRUD 配上幾個驗證規則,DDD 是 over-engineering 的最快捷徑。


下一篇 → DDD 戰術設計

上一篇 → Event Sourcing