Ubiquitous Language(統一語言)

是什麼?

Ubiquitous Language 是 DDD 的基石概念:團隊中所有人(開發者、領域專家、PM)共同建立並使用一套統一的術語來描述業務概念。這套語言不只存在於文件中,更要直接反映在程式碼裡。

ℹ️DDD 核心基礎

Ubiquitous Language 是 DDD 所有戰術與戰略模式的前提。沒有統一語言,Entity、Value Object、Aggregate 等概念都會失去意義。

核心觀念

常見誤區

⚠️常見誤區

誤區一:以為 Ubiquitous Language 只是寫一份名詞對照表就完事。實際上它必須被所有人在日常溝通、程式碼、文件中持續使用。

誤區二:工程師自己發明術語(例如把「庫存」叫做 item_count),沒有和領域專家對齊。程式碼裡的命名必須來自業務語言。

誤區三:試圖建立一個「全公司統一」的語言。Ubiquitous Language 的範圍是 Bounded Context,不是整間公司。

建立流程

1

召集領域專家

找齊業務、PM、開發者,一起開 Event Storming 或需求工作坊

2

識別核心術語

列出所有業務概念,找出同義詞和歧義詞

3

建立 Glossary

每個術語給出明確定義,標註所屬 Bounded Context

4

反映到程式碼

類別名、方法名、變數名全部使用 Glossary 中的術語

5

持續維護

需求變更時同步更新 Glossary 和程式碼命名

程式碼範例

C# 版本

csharp
// ❌ 術語不統一:工程師自己取名
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 版本

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 版本

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())

概念圖

領域專家 (Domain Expert)
共同建立
開發者 (Developer)
共同建立
Ubiquitous Language
記錄於
Glossary(術語表)
程式碼命名
需求文件 / User Story

此圖展示 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 各自維護 |

你可能也想看

Entity(實體)

按 ← → 鍵切換課程