語言分裂的代價#

在缺乏共同語言的專案中,溝通成本極高:

  • 開發者必須替領域專家翻譯技術概念
  • 領域專家必須在開發者與其他專家之間來回轉譯
  • 開發者之間也需要互相翻譯彼此的理解

這種間接溝通帶來的後果相當嚴重:

  • 翻譯過程扭曲模型概念,導致破壞性的程式碼重構
  • 團隊成員對同一術語的理解產生分歧,卻不自知
  • 日常討論的用語與程式碼中的術語脫節
  • 同一個人在口語和書面中使用不同說法,最精確的領域表達往往只出現在短暫的對話中,無法被保留

翻譯會削弱溝通品質,使 Knowledge Crunching 變得貧血。沒有任何一方的專業術語能單獨充當共同語言,因為它們都無法滿足所有需求。

Ubiquitous Language#

核心概念#

專案需要一種比「最大公因數」更強健的共同語言。透過團隊的有意識努力,Domain Model 可以成為這個語言的骨幹,同時連結團隊溝通與軟體實作。這個語言應該在團隊工作中無所不在(ubiquitous)。

Ubiquitous Language 的詞彙來源包括:

  • 類別名稱與重要的操作名稱
  • 模型中已被明確表達的規則相關術語
  • 施加於模型的高層組織原則(如 Context Map、大規模結構)
  • 團隊經常套用於 Domain Model 的模式名稱

模型的關聯性成為語言的組合規則,詞彙和片語的意義呼應模型的語意。

使用範圍#

基於模型的語言應該被使用在:

  • 開發者之間描述系統中的產出物、任務與功能
  • 開發者與領域專家之間討論需求、開發計畫與功能
  • 領域專家之間相互溝通

語言使用得越普遍,理解的流動就越順暢。

語言的演進#

初期的模型可能不夠好,缺乏專業術語的語意豐富度,也缺少開發者在程式碼中創造的更細微、主動的特徵。但語言的持續使用會迫使模型的弱點暴露出來

  1. 團隊會嘗試替換笨拙的用詞或組合
  2. 發現語言的缺口時,新詞彙會進入討論
  3. 語言的變化會被認定為 Domain Model 的變化
  4. 進而更新類別圖、重新命名類別與方法、甚至改變行為
flowchart LR
    A["嘗試替換笨拙詞彙"] --> B["發現語言缺口"]
    B --> C["新詞彙進入討論"]
    C --> D["語言變更 = 模型變更"]
    D --> E["更新類別與方法"]
    E --> A

認知到 Ubiquitous Language 的變化就是模型的變化。 領域專家應反對不恰當的術語或結構;開發者應注意會絆倒設計的模糊性或不一致。

模式宣言#

以模型為語言的骨幹,要求團隊在所有溝通(團隊內部和程式碼中)堅持不懈地鍛鍊這個語言:

  • 在圖表、文件中使用相同語言,尤其是口語
  • 透過嘗試不同表達方式來消除困難,這些表達反映不同的模型
  • 重構程式碼,重新命名類別、方法和模組以符合新模型
  • 在對話中解決術語混淆,就像我們在日常生活中對普通詞彙達成共識一樣

有了 Ubiquitous Language,模型不再只是設計產出物,它成為開發者和領域專家共同工作中不可或缺的一部分。語言以動態形式承載知識,用語言進行的討論讓圖表和程式碼背後的意義活了過來。

範例:Cargo Router 的兩種對話#

Evans 透過兩段對話展示 Ubiquitous Language 的差異。

情境 1:最小化的領域抽象#

使用者提到「change the customs clearance point」時,開發者的回應充滿技術細節:

  • 「刪除 shipment 表中所有該 cargo id 的列」
  • 「將 origin、destination 和新的 customs clearance point 傳入 Routing Service」
  • 「在 Cargo 上放一個 Boolean 來判斷 shipment 表中是否有資料」

對話變得冗長、充滿應用程式功能的解釋,而且容易產生誤溝通。

情境 2:豐富化的 Domain Model 支援討論#

同樣的需求,對話使用了更豐富的模型術語:

  • 改變 Route Specification 中的任何屬性時,刪除舊的 Itinerary,請 Routing Service 根據新的 Route Specification 生成新的
  • 需要在 Route Specification 加入功能:每次 Spec 變更時,檢查 Itinerary 是否仍然滿足 Specification
  • 只有在 Route Specification 不再被滿足時,才會重新產生 Itinerary

第二段對話中,「Itinerary」和「Route Specification」成為雙方可以精確討論的物件,取代了第一段中反覆描述屬性和程序的方式。基於 Domain Model 的術語使對話更精簡,也更準確地傳達了領域專家的意圖。

Modeling Out Loud#

人類對口語有天賦,但在開會時,人們通常不會使用 Domain Model 的語言。他們會用商業術語、外行版本的專業術語,或談論技術產出物和具體功能。

透過口語來精煉模型是最有效的方法之一。嘗試用模型的構件大聲描述場景,粗糙的邊緣很容易「聽」出來。Evans 以 Routing Service 為例,展示三種說法的演進:

  1. 「如果我們給 Routing Service 一個 origin、destination 和 arrival time,它可以查找 cargo 會經過的 stops,然後……把它們塞進資料庫。」(模糊且技術導向)
  2. 「Origin、destination 等等……全部餵給 Routing Service,我們拿回一個 Itinerary,裡面有我們需要的一切。」(更完整,但冗長)
  3. A Routing Service finds an Itinerary that satisfies a Route Specification.」(精簡)

像玩弄文字和片語一樣,將語言能力投入建模工作。這與用圖表運用視覺空間推理、用方法論分析設計是互補的。在所有方式中,語言實驗最常被忽略。

Evans 補充了 Ubiquitous Language 模式的附則:

在談論系統時把玩模型。使用模型的元素和互動來大聲描述場景,以模型允許的方式組合概念。找到更簡潔的說法,再把新想法帶回圖表和程式碼。

One Team, One Language#

技術人員常覺得需要「保護」業務專家,不讓他們接觸 Domain Model:

  • 「對他們來說太抽象了」
  • 「他們不懂物件導向」
  • 「我們必須用他們的術語來收集需求」

Evans 反駁這些理由:

  • 太抽象? 那你怎麼知道抽象是否正確?你對領域的理解有他們深嗎?
  • 領域專家是有能力深入思考自己領域的人。 如果資深的領域專家不理解模型,那是模型有問題
  • 新功能討論初期確實沒有現成的模型可用,但一旦開始與開發者合作,磨合出共享模型的過程就開始了

領域專家使用 Ubiquitous Language 時會:

  • 快速發現模型不足或錯誤之處
  • 發現模型語言的精確性揭露了自己思維中的矛盾或模糊
  • 通過逐步演練場景(walk through scenarios)來非正式地測試模型
  • 用模型語言撰寫 use case 和 acceptance test

語言的多元性有時是必要的,但語言的分界線不應該在領域專家和開發者之間。 開發者有技術術語,使用者也有超出應用程式範圍的專業術語,這些都是語言的延伸。但這些方言不應包含針對同一領域的替代詞彙。

Figure 2.3: UBIQUITOUS LANGUAGE is cultivated in the intersection of jargons.

Ubiquitous Language 培育於各方專業術語的交集處。開發者之間、領域專家之間,以及程式碼中的表達,全部基於從共享 Domain Model 衍生出的同一語言。

Documents and Diagrams#

UML 圖的角色與限制#

Evans 認為簡單、非正式的 UML 圖可以錨定討論:畫出三到五個與當前議題核心的物件,所有人就能保持聚焦。但問題出在人們覺得必須用 UML 傳達整個模型或設計:

  • 過度完整:把所有物件都放進建模工具,見樹不見林
  • 同時遺漏太多:屬性和關聯只是物件模型的一半故事。行為、約束條件不容易用圖表示
  • UML 不是一個令人滿意的程式語言,嘗試用建模工具生成程式碼往往適得其反

模型不等於圖表。 圖表的目的是幫助溝通和解釋模型。程式碼才是設計細節的儲存庫。精心挑選和建構的圖表可以聚焦注意力、輔助導覽,前提是不被「完整呈現模型」的強迫症所淹沒。

Evans 偏好的方式是反轉典型 UML 圖的做法:不是用文字註釋圖表,而是寫文字文件、配上精選且簡化的圖表。

Written Design Documents#

口語溝通為程式碼的嚴謹性和細節補充了意義,但有一定規模的團隊可能需要書面文件的穩定性和可分享性。Evans 提出兩個評估文件的通用準則:

文件應補充程式碼和口語溝通#

  • 程式碼已經提供了細節,它是程式行為的精確規格
  • 其他文件需要闡明意義、提供大規模結構的洞見、聚焦核心元素
  • 書面文件應補充程式碼和對話,而非取代它們

文件應為專案工作並保持即時#

判斷文件是否有用,最簡單的方法是觀察它與 Ubiquitous Language 的互動:

  • 文件是否使用當前專案上人們說的語言?
  • 文件中的術語是否開始出現在對話和程式碼中?
  • 如果文件對 Ubiquitous Language 沒有影響,代表有問題——可能太大、太複雜或主題不夠重要

如果語言自然地演進而文件被拋在後面,代表文件對人們來說不夠相關或不夠重要。繼續保留它不更新只會造成混淆,不如歸檔。透過純粹的意志力和紀律來維護文件是浪費。

Ubiquitous Language 讓需求規格等文件可以更精簡、更少歧義。當 Domain Model 反映了最相關的業務知識,應用程式需求就變成模型中的場景,可以用語言直接連結到 Model-Driven Design。

Executable Bedrock#

XP 社群選擇幾乎完全依賴可執行程式碼和測試。良好撰寫的程式碼確實很有溝通力,但它傳達的訊息不保證準確:

  • 方法名稱可能模糊、誤導或過時
  • 測試中的斷言是嚴謹的,但變數名稱和程式碼組織方式講述的故事不是
  • 寫出不僅「做對的事」而且「說對的話」的程式碼,需要一絲不苟的紀律

要有效溝通,程式碼必須基於撰寫需求所使用的同一語言——開發者彼此之間以及與領域專家對話的語言。

Explanatory Models#

Evans 強調應該有一個模型同時支撐實作、設計和團隊溝通。但模型也可以作為教育工具來傳授領域知識。

技術模型必須嚴格精簡到最小必要範圍,而 Explanatory Model 可以包含提供脈絡的領域面向,補充技術模型的狹窄範圍。它的特點:

  • 可以使用領域專家慣用的視覺隱喻,為開發者提供更清晰的解釋
  • 不需要是物件模型,最好不是
  • 應避免使用 UML,以免與軟體設計產生虛假的對應印象
  • 多元而不同的解釋方式有助於學習

範例:航運操作與路線#

考慮一個追蹤貨物的航運公司應用程式。模型包含港口操作和船舶航次如何組成貨物的營運計畫(route)的詳細視圖。但對不熟悉的人來說,類別圖可能不太容易理解。

Figure 2.4: A class diagram for a shipping route

此時 Explanatory Model 可以幫助團隊成員理解類別圖的實際含義。下圖用另一種方式呈現同樣的概念:圖中每條線代表一次港口操作(裝載或卸載貨物)、貨物在地面倉儲中存放,或貨物在船上運輸途中。它與類別圖不完全對應,但強化了領域的關鍵要點。

Figure 2.5: An explanatory model for a shipping route

這種圖表搭配自然語言的解釋,可以幫助開發者和領域專家理解更嚴謹的軟體模型圖表。兩種視圖結合起來,比單獨任何一種都更容易理解。