# 通用语言

> 团队共享的业务语言，开发者与领域专家使用相同的术语沟通，代码直接反映业务概念。

- Skill: `microwind/skill-101` (Agent Skill)
- Install (CLI): `npx skillmds@latest add microwind/skill-101`
- Raw SKILL.md: https://api.skillmd.com/api/skills/microwind/skill-101/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: microwind (https://skillmd.com/u/microwind)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/microwind/skill-101

---


# 通用语言 (Ubiquitous Language)

## 概述

通用语言是 DDD 的基石。Eric Evans 在蓝皮书中将其定义为：**开发者与领域专家围绕领域模型共同构建的、严谨的（rigorous）共享语言**，贯穿于沟通、文档和代码。

**核心思想**：
- 代码中的命名 = 业务专家使用的术语
- 消除"翻译层"——开发者与业务人员讲同一种话
- 语言的变化意味着模型的变化（反之亦然）
- 双向验证：领域专家应对生硬或不足的术语提出异议；开发者应警觉歧义与不一致
- 持续演进：随着团队对业务理解加深，语言与模型同步精化

## 对比示例

```java
// ❌ 技术语言：开发者能看懂，业务专家看不懂
public class DataProcessor {
    public void processRecord(Map<String, Object> record) {
        if ((int) record.get("type") == 1) {
            record.put("status", 2);
            dbHelper.updateRow("tbl_data", record);
        }
    }
}

// ✅ 通用语言：业务专家也能理解
public class LoanApplication {
    public void approve(Underwriter approvedBy) {
        if (this.status != ApplicationStatus.UNDER_REVIEW) {
            throw new DomainException("只有审核中的申请才能批准");
        }
        this.status = ApplicationStatus.APPROVED;
        this.approvedBy = approvedBy;
        this.approvedAt = LocalDateTime.now();
    }

    public void reject(Underwriter rejectedBy, String reason) {
        this.status = ApplicationStatus.REJECTED;
        this.rejectionReason = reason;
    }
}
```

```python
# ❌ 技术术语
class Handler:
    def handle(self, dto):
        entity = self.repo.get(dto.id)
        entity.flag = True
        self.repo.update(entity)

# ✅ 通用语言
class ClaimService:
    def submit_claim(self, policy_holder_id: str, incident_details: str):
        policy = self.policy_repo.find_active(policy_holder_id)
        claim = policy.file_claim(incident_details)
        self.claim_repo.save(claim)
        return claim
```

## 建立通用语言的方法

```
1. 事件风暴（Event Storming）
   - 用便签列出所有领域事件（"订单已提交"、"支付已完成"）
   - 识别命令（"提交订单"、"确认支付"）
   - 发现聚合和限界上下文

2. 术语表
   | 术语 | 定义 | 上下文 |
   |------|------|--------|
   | 保单 | 保险合同 | 保险 |
   | 理赔 | 保险事故后申请赔偿 | 理赔 |
   | 核保 | 评估风险决定是否承保 | 承保 |

3. 代码即文档
   - 类名、方法名、变量名都使用通用语言
   - 业务专家能读懂代码（至少是方法签名）
```

## 通用语言的约束

```
通用语言在限界上下文内有效：

上下文A（销售）          上下文B（物流）
"订单" = 客户的购买请求   "订单" = 需要配送的包裹
"取消" = 退回商品金额     "取消" = 停止配送流程
"地址" = 收货地址         "地址" = 仓库/中转站地址

→ 不同上下文可以用相同的词表达不同的概念
→ 这正是限界上下文存在的原因
```

## 命名检查清单

```
□ 类名是业务名词（Order, Claim, Policy）而非技术名词（DataObject, Record）
□ 方法名是业务动作（approve, submit, cancel）而非技术动作（update, process）
□ 枚举值反映业务状态（APPROVED, REJECTED）而非数字（1, 2, 3）
□ 业务专家能理解方法签名的含义
□ 代码注释用业务术语解释"为什么"，而非"做什么"
```

## 常见误区

❌ **保留翻译层**——开发用技术词（`user_data`、`record`、`status=2`），业务用业务词（客户、保单、已批准），每次需求评审都在做翻译
→ 把业务术语直接写进代码：类名、方法名、枚举值都向业务专家对齐

❌ **同义词泛滥**——同一概念在不同模块叫 `user` / `customer` / `account` / `member`
→ 同一限界上下文内强制单一术语；不同上下文允许差异，但要在术语表中明确映射关系

❌ **术语表写完即锁柜**——项目启动时维护一次，半年后与代码漂移
→ 把术语表作为活文档，纳入代码评审：新增 / 重命名领域类时同步更新

## 与其他DDD概念的关系

| 概念 | 关系 |
|------|------|
| [限界上下文](../bounded-context/) | 通用语言在限界上下文内有效 |
| [领域模型](../domain-model/) | 通用语言通过领域模型在代码中体现 |
| [领域事件](../domain-events/) | 事件名使用通用语言描述 |

## 总结

**核心**：开发者和业务专家围绕领域模型共建一套严谨、共享、可演进的语言，代码直接反映业务术语。

**实践**：通过事件风暴建立术语表，代码命名使用业务概念，在限界上下文内保持语言一致；遇到生硬或歧义的用语，回到领域专家身边一起修正。

