# 领域驱动设计(Domain-Driven Design) > 本文件是 `dev-expert` 的领域建模专项补充参考,不作为独立子技能计数,也不提供 `@` 显式调用入口。命中「领域建模/限界上下文/聚合根/领域事件/统一语言/防腐层/复杂业务规则建模/遗留系统隔离」等信号,且任务为复杂业务系统(多子域/术语多义/跨上下文协作)时,按需与 `software-project`、`spec-driven-development`、`architecture-decision`、`mysql-database`、`code-generation` 协同加载。CRUD 为主的脚本/营销页/小工具不加载本 reference,避免过度设计(违反 Karpathy 简洁优先)。 ## 适用边界 - 适用:业务规则复杂、术语多义、跨子域协作的中大型系统;遗留系统渐进重构需要隔离新旧模型;需求频繁变更需要稳定领域内核抵御变化。 - 不适用:CRUD 为主、表即业务的小工具/脚本/营销页/简单后台——直接 `mysql-database` + `code-generation` 更高效。 - 判定信号:业务对象 ≥ 5 且存在同名异义、跨模块业务规则联动 ≥ 2、需要对接遗留系统且不想被其模型污染。 ## 核心概念速查 | 概念 | 定义 | 与现有 reference 的衔接 | | ------------------------------- | ------------------------------------------------ | ------------------------------------------------------------- | | 统一语言(Ubiquitous Language) | 需求→代码全程使用同一术语,消除翻译损耗 | 贯穿 `project-memory-management` Glossary,强制约束而非仅记录 | | 实体(Entity) | 有唯一身份、生命周期内身份不变的对象 | `mysql-database` 表设计前先识别实体 | | 值对象(Value Object) | 无身份、由属性值定义、不可变 | 金额/地址/坐标等用值对象,减少表与 ORM 复杂度 | | 聚合根(Aggregate Root) | 一致性边界入口,外部只能持有根的引用 | `mysql-database` 事务边界:一个事务只改一个聚合 | | 领域事件(Domain Event) | 表示领域中已发生的事实,用于解耦上下文 | `spec-driven-development` Scenario 的业务侧表达 | | 限界上下文(Bounded Context) | 模型的语义边界,同一术语在不同上下文可有不同含义 | 补 `software-project` 模块边界审查的语义维度 | | 上下文映射(Context Map) | 描述上下文间关系(ACL/共享内核/客户-供应商等) | 补 `architecture-decision` 的集成模式选择 | ## 执行流程 ### 第一步:建立统一语言 - 从需求描述提取领域名词,与业务方确认含义,消除同名异义/异名同义 - 术语写入 `project-memory-management` Glossary,标注所属限界上下文 - 代码命名必须与统一语言一致(`Order` 在销售上下文 = 订单,在物流上下文 = 运单,须区分命名如 `SalesOrder`/`ShipmentOrder`) ### 第二步:事件风暴识别领域事件 - 用领域事件倒推业务流程:先列"什么发生了"(OrderPlaced/PaymentReceived/OrderShipped),再连成流程 - 每个事件标注:触发者、相关实体、后续影响 - 事件命名用过去式,表示已完成的事实 ### 第三步:识别聚合与聚合根 - 按"一致性边界"分组:必须强一致的对象归入同一聚合,根为外部访问入口 - 聚合设计原则: - 小聚合优先:一个聚合根 + 少量值对象/内部实体,避免大聚合导致大事务 - 跨聚合用引用(存 ID)而非直接对象持有 - 聚合内业务逻辑内聚到根或内部实体(充血模型),禁止贫血模型 - 一个事务只修改一个聚合;跨聚合一致性用领域事件 + 最终一致 ### 第四步:划定限界上下文 - 按语义边界切分,而非技术分层:同一术语在不同上下文可有不同模型 - 上下文边界对齐团队/子系统/部署单元,便于独立演进 - 输出上下文清单:上下文名 / 核心领域语言 / 对外接口 / 依赖的其他上下文 ### 第五步:上下文映射与集成模式选择 对每对交互的上下文,选择集成模式(权衡走 `architecture-decision` 流程并附反选论证): | 模式 | 适用场景 | 代价 | | ------------------------- | --------------------------------------------- | ------------------------------ | | 防腐层(ACL) | 接入遗留系统/外部不可控 API,避免被其模型污染 | 多一层翻译层,增加复杂度 | | 共享内核 | 多上下文必须共享同一子模型 | 共享部分变更需多方协调 | | 客户-供应商 | 上游(供应商)服务下游(客户),双方可协商 | 需协商机制,强势上游会压制下游 | | 遵奉者(Conformist) | 下游被迫遵循上游模型 | 下游丧失模型自主权 | | 各行其道(Separate Ways) | 上下文无实际耦合,独立演进 | 可能重复建模,但解耦彻底 | ### 第六步:领域模型落地实现 - 聚合根作为事务与一致性入口,外部通过根的方法操作内部对象 - 领域事件在聚合根方法内产生,由应用服务/事件分发器发布 - 应用服务编排聚合、事务、事件发布;领域服务承载跨聚合的无状态领域逻辑 - 仓储(Repository)按聚合根粒度提供持久化,禁止跨聚合 JOIN 式仓储 - 与 `code-generation` 协同:生成代码须体现充血模型与聚合边界,禁止产出贫血模型 + 巨型 Service ### 第七步:验证与沉淀 - 领域规则用单元测试覆盖聚合根方法(不依赖数据库) - 跨上下文协作用集成测试覆盖事件流 - 领域模型与术语沉淀到 `project-memory-management`,作为后续变更的基准 ## 架构规则 1. 一个事务只修改一个聚合根;跨聚合一致性用领域事件 + 最终一致,禁止跨聚合大事务。 2. 聚合内业务逻辑内聚到领域对象(充血模型),禁止贫血模型(只有 getter/setter 的实体)。 3. 外部只能持有聚合根的引用,聚合内部对象不可直接被外部访问。 4. 跨聚合引用只存 ID,禁止对象直接持有。 5. 领域事件在领域层产生、在应用层发布,禁止领域层直接依赖消息中间件实现。 6. 防腐层是必选项而非可选项,当对接遗留/外部系统时必须建立,禁止领域模型直接被外部模型渗透。 7. 统一语言是硬约束:代码命名与 Glossary 不一致即视为缺陷,须在 `code-review` 阶段拦截。 8. 新建聚合/上下文必须有真实业务驱动,禁止 speculative 建模(呼应 Karpathy 简洁优先)。 ## 常见坑与反模式 | 场景 | 风险 | 处理方式 | | ---------------------------- | ------------------------------------------ | --------------------------------------------------------- | | 贫血模型 + 巨型 Service | 业务逻辑散落、领域对象沦为 DTO | 行为内聚到聚合根,Service 只做编排 | | 跨聚合事务 | 大事务、锁竞争、一致性边界模糊 | 拆为聚合内事务 + 领域事件最终一致 | | 大聚合(一个根聚合数十实体) | 事务过大、加载性能差 | 拆小聚合,跨聚合用 ID 引用 | | 直接持有外部上下文对象 | 模型耦合、上下文边界失效 | 只存 ID + 防腐层翻译 | | 领域事件同步阻塞发布 | 事务回滚但事件已发出、或事件失败拖垮主流程 | 事务提交后再发布(如 `DB::afterCommit` / 事务后派发) | | 为 CRUD 场景强加聚合/事件 | 过度设计、违反简洁优先 | 适用边界外场景直接走 `mysql-database` + `code-generation` | | 共享内核无节制扩张 | 多上下文被绑定、变更困难 | 共享内核限制在极小稳定子集,其余走 ACL | | 统一语言仅记录不约束 | 术语漂移、模型与需求脱节 | `code-review` 阶段强制核对命名与 Glossary 一致 | ## 关联 reference | 协同点 | 说明 | | ----------------------------------------- | ----------------------------------------------------------------------------------------------------- | | `software-project` 第三步点五模块边界审查 | 本 reference 提供语义边界维度(限界上下文),与工程边界(依赖/接口/数据持有)互补;审查时先语义后工程 | | `mysql-database` | 表设计前先识别实体/值对象/聚合根;事务边界对齐聚合边界 | | `spec-driven-development` | Scenario 可用领域事件表达;领域模型作为 spec Design 层的核心产物 | | `architecture-decision` | 上下文映射集成模式选择走架构决策流程,附反选论证 | | `code-generation` | 生成代码须体现充血模型与聚合边界;应用服务/领域服务/仓储分层 | | `code-review` | 审查贫血模型、跨聚合事务、统一语言一致性、防腐层缺失 | | `refactoring` | 遗留系统重构优先建立防腐层再逐步抽取领域模型 | | `project-memory-management` | 统一语言写入 Glossary 并标注所属上下文;领域模型作为决策记录沉淀 | ## 质量标准 - 限界上下文必须有明确的语义边界和核心领域语言,不能按技术分层切分 - 聚合根必须是一致性边界入口,外部不可绕过根访问内部对象 - 跨聚合一致性必须显式选择事务或最终一致,禁止隐式跨聚合事务 - 统一语言必须贯穿需求→代码,Glossary 与代码命名一致 - 对接遗留/外部系统必须有防腐层,领域模型不可被外部模型直接渗透 - 领域规则必须有单元测试覆盖(不依赖数据库) - 适用边界外的简单场景不得强加 DDD 模式(呼应 Karpathy 简洁优先) ## 失败回退机制 | 步骤 | 失败条件 | 回退目标 | 最大重试 | 不可恢复时升级路径 | | ---------------------- | ----------------------------------------------- | ------------------------------------ | -------- | --------------------------------------- | | 第一步:建立统一语言 | 业务方术语含义不清或存在分歧 | 列出 2-3 种理解,请业务方确认 | 2 | 标注待确认术语,暂用占位命名 | | 第二步:事件风暴 | 业务流程无法连成事件链 | 退回第一步补全统一语言 | 1 | 输出流程断点清单交业务方补充 | | 第三步:识别聚合 | 聚合边界难以判定(强一致 vs 最终一致不明) | 按变更频率与一致性要求分组,标注假设 | 2 | 走 `architecture-decision` 决策聚合边界 | | 第四步:划定限界上下文 | 上下文边界与现有模块/团队结构冲突 | 输出语义边界 vs 工程边界冲突清单 | 1 | 交架构师/业务方拍板 | | 第五步:上下文映射 | 集成模式难以取舍 | 走 `architecture-decision` 反选论证 | 1 | POC 验证后由用户拍板 | | 第六步:领域模型落地 | 与现有框架/ORM 冲突(如 Eloquent 倾向贫血模型) | 输出冲突点 + 妥协方案 | 1 | 标注已知限制,部分内聚部分编排 | | 第七步:验证与沉淀 | 领域规则测试难以隔离数据库 | 用内存仓储/Factory 隔离 | 2 | 标注未隔离测试项 |