Ubiquitous Language(統一語言)
是什麼?
Ubiquitous Language 是 DDD 的基石概念:團隊中所有人(開發者、領域專家、PM)共同建立並使用一套統一的術語來描述業務概念。這套語言不只存在於文件中,更要直接反映在程式碼裡。
ℹ️DDD 核心基礎
Ubiquitous Language 是 DDD 所有戰術與戰略模式的前提。沒有統一語言,Entity、Value Object、Aggregate 等概念都會失去意義。
核心觀念
- 術語不一致 = Bug 的溫床:業務說「訂單」,工程師寫
Transaction,QA 測「購買紀錄」— 三個詞指同一件事,但每次溝通都要翻譯,翻譯就會出錯。具體例子:業務說「取消訂單」,工程師理解為「軟刪除」,但實際上業務意思是「退款 + 恢復庫存 + 通知物流」。一個術語的分歧就可能導致一整條業務流程做錯 - 程式碼即文件:變數名、方法名、類別名都要使用 Ubiquitous Language,讀程式碼就像讀業務規格書。判斷標準:把你的類別名和方法名唸給業務人員聽,如果他們聽得懂,就是好的命名
- Glossary 是活文件:建立術語表並持續維護,新進成員靠它上手,老成員靠它對齊。Glossary 的格式建議:術語名稱 + 定義 + 所屬 Bounded Context + 範例 + 同義詞/易混淆詞。存放位置:Wiki 或 repo 的
docs/glossary.md - 語言隨 Bounded Context 變化:同一個詞在不同 Context 可能有不同意義。例如「Product」在銷售 Context 有價格和折扣規則,在倉儲 Context 有重量和儲位,在行銷 Context 有 SEO 描述和分類標籤。每個 Context 各自維護自己的定義,不要強迫統一
常見誤區
⚠️常見誤區
誤區一:以為 Ubiquitous Language 只是寫一份名詞對照表就完事。實際上它必須被所有人在日常溝通、程式碼、文件中持續使用。
誤區二:工程師自己發明術語(例如把「庫存」叫做 item_count),沒有和領域專家對齊。程式碼裡的命名必須來自業務語言。
誤區三:試圖建立一個「全公司統一」的語言。Ubiquitous Language 的範圍是 Bounded Context,不是整間公司。
建立流程
召集領域專家
找齊業務、PM、開發者,一起開 Event Storming 或需求工作坊
識別核心術語
列出所有業務概念,找出同義詞和歧義詞
建立 Glossary
每個術語給出明確定義,標註所屬 Bounded Context
反映到程式碼
類別名、方法名、變數名全部使用 Glossary 中的術語
持續維護
需求變更時同步更新 Glossary 和程式碼命名
程式碼範例
C# 版本
// ❌ 術語不統一:工程師自己取名
public class Txn
{
public decimal Amt { get; set; }
public string Cust { get; set; }
public int Qty { get; set; }
}
// ✅ 使用 Ubiquitous Language
public class Order
{
public OrderId Id { get; }
public Customer Customer { get; }
public Money TotalAmount { get; private set; }
public IReadOnlyList<OrderLineItem> LineItems => _lineItems.AsReadOnly();
private readonly List<OrderLineItem> _lineItems = new();
public void AddLineItem(Product product, Quantity quantity)
{
var lineItem = new OrderLineItem(product, quantity);
_lineItems.Add(lineItem);
TotalAmount = CalculateTotal();
}
public void Place()
{
if (!_lineItems.Any())
throw new InvalidOperationException("Cannot place an order with no line items.");
// 「下單」這個動作,業務和工程師都叫 Place Order
}
private Money CalculateTotal() =>
_lineItems.Aggregate(Money.Zero, (sum, item) => sum + item.Subtotal);
}TypeScript 版本
// ❌ 術語不統一
interface Txn {
amt: number;
cust: string;
qty: number;
}
// ✅ 使用 Ubiquitous Language
interface OrderLineItem {
product: Product;
quantity: Quantity;
subtotal: Money;
}
class Order {
private lineItems: OrderLineItem[] = [];
constructor(
public readonly id: OrderId,
public readonly customer: Customer,
) {}
addLineItem(product: Product, quantity: Quantity): void {
this.lineItems.push({
product,
quantity,
subtotal: product.price.multiply(quantity.value),
});
}
place(): void {
if (this.lineItems.length === 0) {
throw new Error("Cannot place an order with no line items.");
}
// 業務術語直接成為方法名
}
get totalAmount(): Money {
return this.lineItems.reduce(
(sum, item) => sum.add(item.subtotal),
Money.zero(),
);
}
}Python 版本
from dataclasses import dataclass, field
from typing import List
# ❌ 術語不統一
class Txn:
def __init__(self, amt, cust, qty):
self.amt = amt
self.cust = cust
self.qty = qty
# ✅ 使用 Ubiquitous Language
@dataclass
class OrderLineItem:
product: "Product"
quantity: "Quantity"
@property
def subtotal(self) -> "Money":
return self.product.price * self.quantity.value
@dataclass
class Order:
id: "OrderId"
customer: "Customer"
_line_items: List[OrderLineItem] = field(default_factory=list)
def add_line_item(self, product: "Product", quantity: "Quantity") -> None:
self._line_items.append(OrderLineItem(product, quantity))
def place(self) -> None:
if not self._line_items:
raise ValueError("Cannot place an order with no line items.")
@property
def total_amount(self) -> "Money":
return sum((item.subtotal for item in self._line_items), Money.zero())概念圖
此圖展示 Ubiquitous Language 的三向同步:領域專家和開發者共同建立統一語言,這套語言同時反映在 Glossary、程式碼命名、和需求文件中。三者必須保持一致——如果 Glossary 寫「退款」但程式碼寫 updateBalance,就代表語言不統一。
實戰補充
💡資深開發者筆記
在實務專案中,最有效建立 Ubiquitous Language 的方式是 Event Storming:用便利貼把所有業務事件貼在牆上,過程中自然會發現術語不一致的地方。每次發現分歧,就當場討論並決定統一用語,然後記進 Glossary。
另一個技巧:在 Code Review 時檢查命名是否符合 Ubiquitous Language。如果有人寫了 updateStatus(),但業務術語是「核准訂單」,就應該改成 approveOrder()。
理解測驗
🤔 為什麼 Ubiquitous Language 是 DDD 的基礎?
🤔 以下哪個做法違反了 Ubiquitous Language 原則?
🤔 Ubiquitous Language 的適用範圍是?
重點整理
💡一句話記住
口訣:「開會怎麼說、程式碼就怎麼寫」—— 如果業務說退款你就寫 refund(),不是 updateBalance()。
| 概念 | 說明 | |------|------| | Ubiquitous Language | 團隊共同建立的統一業務術語 | | Glossary | 記錄所有術語定義的活文件 | | 程式碼即文件 | 類別名、方法名直接使用業務術語 | | 核心好處 | 消除溝通鴻溝,減少因術語不一致造成的 Bug | | 適用範圍 | 每個 Bounded Context 各自維護 |