Ubiquitous Language 不是 DDD 的装饰术语
Ubiquitous Language 听起来很像那种 DDD 演讲里必须出现的词,好让幻灯片显得足够“架构”。
但它不是装饰。
Ubiquitous Language 是软件项目里非常重要的保护机制之一。它保护团队免受误解、错误模型、失真的 API,以及那些 UI 文案和代码表达完全不同含义的问题。它甚至能预防一类看起来像技术 Bug、实际上只是语言事故顺便带上了 Deployment Pipeline 的问题。
核心思想其实很简单:
一个团队需要一套共同语言,来描述它正在处理的业务领域。
不是随便一套项目黑话,不是 Wiki 里放几个词条,也不是“业务这么叫,IT 那么叫,不过大家都知道是什么意思”。
而是一套有意识连接业务、Product、Development、Test、UI、API 与 Code 的语言。
这就是 Ubiquitous Language。
为什么语言在 DDD 中如此核心
Section titled “为什么语言在 DDD 中如此核心”Domain-driven Design 首先并不是 Pattern。
不是 Aggregate。 不是 Repository。 不是 Value Object。 也不是把一些 Tactical Building Block 扔进项目里,就能让它显得更成熟。
DDD 从 Domain 开始。
也就是 Problem Space:业务规则、术语、流程、例外与系统需要表达的意义。
语言正是在这里变得关键。
如果连业务概念叫什么都没有共识,你怎么构建一个好的 Domain Model?如果同一个词在三个团队里表示三件事,你怎么划分边界?如果没人能准确说清一个对象在业务上代表什么,你又怎么设计稳定的 API?
Model 并不是从代码开始的。
Model 先存在于语言里。
代码只是后来那个更精确、也更不宽容的版本。
语言模糊,模型就会模糊。模型模糊,代码最终一定会把这种模糊暴露得非常诚实。
Ubiquitous 并不意味着所有地方都必须叫同一个名字
Section titled “Ubiquitous 并不意味着所有地方都必须叫同一个名字”先澄清一个常见误解:Ubiquitous Language 并不要求系统的每个地方都出现完全相同的词。
UI Label 可以比内部业务术语更符合用户语言。 API Field 可以比 Button 更技术化。 Data Model 有自己的约束。 团队日常交流也可能比一个精确的 Domain Term 更简短。
这些并不自动构成问题。
真正的问题是:差异是否只是偶然产生的。
Customer 用一个词,Product Owner 用第二个,Backend 用第三个,Frontend 用第四个,Database 再用第五个——然后所有人都假装这只是个人偏好。
不是。
到了这一步,我们已经无法确定:不同名称是否指向同一概念,或者同一个名称是否指向不同概念。
两种情况都很危险。
所以 Ubiquitous Language 不是语言上的“一刀切”。
它要求的是有意识的映射。
必须能回答:
- 业务术语是什么?
- UI 如何展示它?
- API Contract 里叫什么?
- Code 里叫什么?
- 这个含义在哪个 Context 内成立?
- 哪里发生翻译?
- 哪里绝对不能悄悄翻译?
如果这些关系说不清,得到的不是灵活系统,而是一团带 Autocomplete 的语义雾。

Statement 的故事
Section titled “Statement 的故事”有一次,我被拉进了一场 Bug 讨论。
Call 里有 Frontend Developer、Backend Developer、外部客户、内部 Stakeholder、Product Owner,最后还有作为 Architect 的我。会议要素非常齐全:一个 Bug、六种观点,以及一个名叫 Statement 的对象。
所有人都在讨论这个 Statement。
遗憾的是,没有人真正指的是同一件东西。
客户在业务上谈的是一份服务结算。IT 里某个时候把它叫成了 Accounting Statement。为了省时间,日常沟通里最后只剩下 Statement。
与此同时,Frontend 在另一个地方又建模了真正意义上的“发票”——业务上更接近 Invoice。但那个对象也叫 statement。
也许因为长得差不多,也许因为代码里已经有一个 statement,也许只是“历史上就是这么长出来的”。每次听到这种话,我都知道某个旧的架构事故大概率被压在地板下面。
最后结果是:
客户说的是服务结算。 Backend 说的是 Accounting Statement。 Frontend 想的是 Invoice。 PO 想的是某个 Process State。 部分 Stakeholder 想的是 Report。
所有人都使用同一个词。
可惜并没有讨论同一个东西。
这就是关键。
问题不在于 Statement 是英文。英文不是坏事,德文也救不了架构。
问题在于这个词已经不再代表一个清晰的业务概念。
它变成了多个含义的容器。
一个词如果什么都可以表示,那么在代码里它就再也无法可靠地表示某一件事。

这不是一个简单的 Naming 问题
Section titled “这不是一个简单的 Naming 问题”有人可能会说:“那把名字改正确不就行了?”
是,也不完全是。
只改名字太浅了。
错误名称往往只是更深层问题的可见症状:团队实际上没有共同的业务模型。
Name 不只是 Cosmetic。
Name 会携带假设。
一个对象叫 Invoice,我会期待它是一张发票。
叫 Statement,我会期待某种对账、说明或报表。
叫 BillingReport,我会期待一个 Report。
叫 Settlement,我会期待一个结算或清算过程。
这些预期会影响 Developer 如何写代码,Tester 如何写 Test Case,Product Owner 如何写 Acceptance Criteria,Stakeholder 如何理解 Requirement,用户如何理解 UI。
Name 是 Architecture。
不是因为好名字看起来更优雅,而是因为名字会划分边界、表达职责、稳定含义——或者把含义彻底搅混。
懒得把概念说清楚,不是一条架构原则。
为什么这在 Frontend 尤其痛
Section titled “为什么这在 Frontend 尤其痛”语言问题往往最先在 Frontend 变得明显。
Frontend 位于 User、Product、Design、Backend 与 API 的交界处。它必须表达业务,指导输入,解释状态,翻译错误,还经常需要把多个技术模型组合成一个用户能理解的 ViewModel。
如果业务语言模糊,Frontend 就会变成替其他建模失误擦屁股的翻译公司。
API Field 叫 statement,UI 显示“发票”,业务部门说“服务结算”,表单里写“结算证明”,代码里又有一个 StatementViewModel,而这个 ViewModel 根据 Route 不同还表示不同东西。
技术上当然都能做。
你也可以给潮湿的地下室重新贴墙纸。
问题只是:你准备假装问题出在墙纸上多久?
因此,Front-end Architecture 尤其需要清晰语言。不是因为 Frontend 听起来“离业务近”,而是因为 Product Model、User Understanding 与 Technical Contract 不一致时,Frontend 最容易暴露这种错位。
Bounded Context 让差异变得可见
Section titled “Bounded Context 让差异变得可见”当然,同一个词在不同 Context 中完全可以有不同含义。
Identity Context 里的 Account 不一定等于 Accounting Context 里的 Account。Banking 里的 Statement 可以和服务结算 Context 里的 Statement 完全不同。CRM 里的 Customer 也不一定和 Support 或 Billing 里的 Customer 是同一个 Model。
这些都没问题。
前提是边界可见。
这也是 Bounded Context 的真正价值之一:不是一个时髦架构术语,而是给“意义”搭一道围栏。
在一个 Context 内,术语应该足够精确。
跨越 Context Boundary 时可以翻译,但翻译必须显式发生。
不能通过一个到处传递的 DTO 偷偷发生。
不能通过一个号称“中立”的 Shared Model 把所有模糊语义集中起来。
更不能继续用一个什么都能表示的 Statement,只是因为没人愿意做决定。
不同 Context 里一个词有不同含义,并不是问题。
真正的问题是没人知道当前究竟在哪个 Context 里说话。
语言已经坏掉的警告信号
Section titled “语言已经坏掉的警告信号”项目里出现下面这些句子时,应该立即提高警惕:
- “我们历史上就是这么叫的。”
- “客户那边叫法不一样。”
- “Frontend 和 Backend 里名字不一样。”
- “其实它是一张 Invoice,不过代码里叫 Statement。”
- “很难解释,但大家都知道什么意思。”
- “这是个集合对象。”
- “这个字段要根据情况解释。”
- “技术上是一个东西,业务上又不完全是。”
- “以后再统一命名。”
- “大家都知道什么意思。”
最后一句尤其危险。
“大家都知道什么意思”经常只表示:大家已经学会忍受同一种模糊。
这不是共同理解。
这只是带着 Sprint Goal 的集体妥协。
可以具体做什么
Section titled “可以具体做什么”Ubiquitous Language 不需要从一个大型 DDD Workshop 开始。
很多时候,只需要在正确的时刻慢一点。
尤其是在激烈的 Bug Discussion 里,不要马上跳进 Log、JSON Payload 或责任归属。
先问:
我们现在说的真的是同一个东西吗?
面对一个叫 Statement 的对象,可以继续问:
Statement of what? 给谁? 发生在哪个 Process? 这个对象的业务职责是什么? 它是 Invoice?Settlement?Report?Status?Document?Booking Information?
如果不同人给出了不同答案,那其实找到了一件很有价值的东西。
不只是当前 Bug,而是一整类 Bug 的潜在根源。
实践中,一些简单规则已经很有帮助:
- 按 Context 定义术语
- 不要因为省事而稀释 Domain Term
- Code Object 按业务职责命名
- API Contract 避免模糊的集合词
- 翻译时确认语义是否仍然一致
- UI Label 与 Domain Term 的映射要有意识
- Glossary 保持轻量,但要持续维护
- 一个词有多个含义时,显式建模,不要继续绕过去
目标不是 Language Police。
目标是让 Domain 足够稳定,能够可靠地构建软件。
共同语言就是架构工作
Section titled “共同语言就是架构工作”Ubiquitous Language 不是一个装饰性的 DDD Term。
它连接 Domain 与 Code,连接 Customer 与 Team,连接 Product Decision 与 Implementation,也连接“我们真正想表达什么”和“系统最后构建了什么”。
如果这条连接断掉,产生的并不是小小的 Naming 问题。
会出现错误 Model、错误 API、错误 UI,以及一类所有人都很难立刻理解的 Bug——因为大家用相同的词,脑中却是不同的图景。
所以语言本身就是架构工作。
不是那种有 Diagram 和 Arrow 的漂亮架构工作。
更多时候,是会议中比较尴尬的那一种:
“等一下。我们说 Statement 时,到底具体指什么?”
然后房间里突然安静。
这通常是好事。
所有人都使用同一个词,并不代表他们在讨论同一个 Model。
Ubiquitous Language 也不是从 Glossary 开始的。
它开始于有人问出这句话的那一刻:
我们现在说的,真的是同一个东西吗?