← 返回文章列表

DDD 实战(二):战略设计,从业务语言到限界上下文

2026年6月24日架构设计
DDD战略设计限界上下文架构设计

DDD 实战(二):战略设计,从业务语言到限界上下文

推荐 BeWild 代充 Codex ,工作猛猛提效。

DDD 的战略设计,解决的是大系统的结构问题。

它不急着问类怎么写、表怎么建、Repository 放在哪里,而是先问几个更上游的问题:

  • 这个业务到底由哪些领域构成?
  • 哪些能力决定产品竞争力?
  • 哪些能力只是支撑主业务运行?
  • 哪些能力可以复用成熟方案?
  • 哪些概念必须使用同一套模型?
  • 哪些概念虽然名字一样,但在不同语境下含义不同?

这些问题没有回答清楚,后面的战术设计和代码分层都会变形。

1. 领域:业务问题的边界

领域可以先粗略理解为“系统要解决的业务问题空间”。

电商有交易领域、支付领域、履约领域、库存领域、营销领域。支付系统有收银台、支付单、渠道路由、清结算、账务、风控。协同办公 IM 有账号、组织、关系、群组、消息、通知、工作台、审批、开放平台、搜索、安全治理。

注意,领域不是技术模块。

“MySQL”“Redis”“MQ”“HTTP 网关”不是领域,它们是实现手段。“用户表”“消息表”“群表”也不是天然领域,它们只是某个模型的持久化形态。

领域划分应该先从业务能力出发,再落到系统边界。Microsoft 的领域分析文档也强调,微服务边界应围绕业务能力,而不是围绕数据访问、消息等水平技术层。Microsoft: Domain analysis for microservices

2. 子域:不是所有业务都同样重要

一个大领域可以继续拆成多个子域。DDD 通常把子域分成三类:核心域、支撑域、通用域。

核心域 是产品差异化和竞争力所在。它值得投入最强的建模精力,不能轻易外包给通用方案。

支撑域 是业务运行必需,但不是核心竞争力。它通常需要定制开发,但建模深度和投入优先级低于核心域。

通用域 是行业里已经有成熟解法的能力。能买就买,能复用就复用,不要把团队最宝贵的注意力耗在这里。

以协同办公 IM 为例,如果我们把它定位成“企业沟通协作平台”,那核心域可能包括:

  • 会话与消息。
  • 组织身份与通讯录。
  • 群组协作。
  • 开放平台生态。
  • 安全治理和权限体系。

支撑域可能包括:

  • 通知。
  • 搜索。
  • 日程。
  • 审批。
  • 工作台应用入口。

通用域可能包括:

  • 短信验证码。
  • 基础文件预览。
  • 通用对象存储。
  • 第三方登录协议适配。

这个分类不是永久不变的。如果产品战略变了,子域分类也会变。比如一个普通 IM 产品里,审批可能只是支撑域;但如果产品转向深度 OA 协同,审批和流程编排就可能上升为核心域。

3. 统一语言:模型先进入语言,再进入代码

DDD 非常重视统一语言。所谓统一语言,不是写一份术语表放在文档里,而是让业务、产品、开发、测试在同一个上下文里使用同一套词,并把这套词落实到代码、接口、事件、表名和文档中。

协同办公 IM 里,“用户”这个词就应该被拆开看。

在账号上下文里,我们可能说:

  • Account:一个可登录账号。
  • Credential:账号凭证。
  • Device:登录设备。
  • LoginSession:登录会话。

在组织上下文里,我们说:

  • Tenant:企业或租户。
  • Department:部门。
  • Member:组织成员。
  • OrgRole:组织角色。

在关系上下文里,我们说:

  • ContactRelation:联系人关系。
  • FriendRequest:好友申请。
  • BlockRelation:拉黑关系。
  • ExternalContact:外部联系人。

在群组上下文里,我们说:

  • Group:群。
  • GroupMember:群成员。
  • GroupRole:群角色。
  • GroupPolicy:群治理规则。

这些词背后都是“人”,但它们不是同一个模型。强行复用一个 User,会让语言变模糊,也会让代码变脆。

4. 限界上下文:模型有效的范围

限界上下文是战略设计里最关键的概念之一。

Martin Fowler 对 Bounded Context 的解释是:一个模型只在特定上下文内保持一致和明确,不同上下文之间需要显式映射。Martin Fowler: Bounded Context

换句话说,限界上下文回答的是:这套模型在哪个范围内成立?

比如“成员”在组织上下文里,关注的是企业、部门、岗位、入职状态、组织角色、字段可见性。在群组上下文里,关注的是群昵称、群角色、入群方式、禁言状态、最后阅读位置。它们都可以关联到同一个现实中的人,但模型关注点完全不同。

如果两个上下文都叫 Member,也可以接受,只要它们处在不同包、不同服务或不同模块边界内,并且交互时使用明确的防腐层或 DTO。问题不在于重名,而在于没有边界。

5. 上下文映射:边界之间怎么协作

限界上下文不是孤岛。复杂系统的关键恰恰在于上下文之间如何协作。

DDD 里常见的上下文关系包括:

  • 客户方/供应方:一个上下文依赖另一个上下文提供能力。
  • 开放主机服务:上游上下文用稳定 API 暴露能力。
  • 发布语言:上下文之间使用约定好的接口、事件或协议沟通。
  • 防腐层:下游上下文通过转换层隔离上游模型。
  • 各行其是:两个上下文没有必要集成,保持独立演进。

协同办公 IM 中,组织上下文和消息上下文就不应该共享组织表。更合理的方式是:

  1. 组织上下文负责组织成员事实。
  2. 组织上下文发布 OrganizationMemberChanged 事件。
  3. 消息上下文维护自己需要的成员快照或权限缓存。
  4. 消息发送时只依赖消息上下文内的发送规则。
  5. 权限强校验需要调用组织上下文时,通过明确接口完成。

这样做牺牲了一些实时一致性和实现简单性,但换来了模型边界和故障隔离。组织结构频繁同步时,不会把消息主链路拖死。

6. 核心域、支撑域、通用域如何影响投入

DDD 的一个重要价值,是帮团队决定“哪里值得复杂,哪里必须简单”。

如果会话与消息是核心域,就值得认真设计:

  • 消息 ID 与幂等。
  • 消息状态机。
  • 已读未读模型。
  • 撤回、编辑、引用、转发规则。
  • 大群扇出策略。
  • 多端同步游标。
  • 加密消息和普通消息的差异。

如果短信验证码是通用域,就不应该投入大量模型设计。封装一个清晰的 provider 接口,接入供应商即可。

如果审批是支撑域,但未来可能成为核心域,那早期可以先保持模块化边界,不必马上设计成独立流程引擎。等审批规则、表单编排、自动化流转真正成为产品竞争力,再提升建模投入。

战略设计的价值就在这里:它不是让所有模块都“DDD 化”,而是让团队知道哪些地方必须精细建模,哪些地方保持朴素即可。

7. 没有标准答案,但有判断依据

领域划分没有唯一标准答案。尤其在账号、组织、关系、群组这些边界上,不同团队很容易给出不同方案,而且各有道理。

比如好友关系应该放在账号域吗?

如果产品很简单,好友只是账号之间的一个双向关系,放在账号模块里可以接受。这样实现快、调用少、事务简单。

但如果好友关系逐渐演化出申请、备注、分组、黑名单、外部联系人、客户关系、关系权限、隐私设置,它就不再只是账号的附属字段,而是一个独立关系上下文。

再比如群聊应该属于消息域吗?

如果群只是消息投递目标,把群放在消息模块里似乎合理。但如果群有群主、管理员、入群审批、群公告、群禁言、群机器人、群文件、群权限,群组模型就应该独立出来,消息域只消费“可发送到某个会话”的结果。

所以判断一个边界是否合理,要看:

  • 业务语言是否清晰。
  • 变化是否能局部化。
  • 一致性边界是否自然。
  • 团队职责是否匹配。
  • 部署和调用成本是否可接受。
  • 未来演进时是否有可迁移路径。

DDD 的严肃性不是“切得最细”,而是“切得有理由”。

8. 战略设计的产出应该是什么

一次有价值的战略设计,至少应该产出这些东西:

  • 领域全景图。
  • 子域分类:核心域、支撑域、通用域。
  • 限界上下文列表。
  • 每个上下文的职责、不应承担的职责。
  • 上下文之间的关系图。
  • 关键领域事件。
  • 数据所有权说明。
  • 当前阶段的拆分优先级。

这些产出不一定都要画成正式 UML。白板、表格、Markdown、Mermaid 图都可以。关键是团队能围绕这些产出讨论,并且在代码和部署里逐步兑现。

下一篇我们进入战术设计,看实体、值对象、聚合根、领域服务和领域事件如何把战略边界落到代码模型中。

返回博客列表