ddd-modeling
Skill4 starsChanged 8 months agoApache-2.0View on GitHub
对一个业务领域进行 DDD 建模时使用。按模块组织:先确定模块名,再按"需求 → 用例分析 → 领域事件 → 领域命令 → 领域事件监听器 → 读模型 → 索引排查 → 聚合 → 聚合字段 → 性能排查"推进,产出置于 ddd-modeling/<module>/ 下。当用户要为新业务或子系统设计领域模型、或修订现有领域模型时启用。
3 other projects use a version of this.
Adopted by
Most recently taken up by
- EmeaAppGbb/spec2cloudtheir own version, first seen Oct 9, 2026
- gabriellst/codmtheir own version, first seen Aug 26, 2026
- crgarcia12/Liliputtheir own version, first seen Jul 14, 2026
Other versions3 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 3
- EmeaAppGbb/spec2cloud★ 100same name
- crgarcia12/Liliput★ 4same name
- gabriellst/codm★ 4same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repositoryOne other file it gives its agents.
The file
Files
DDD 领域建模
本 skill 沉淀了领域建模的过程与方法,供后续对新业务领域建模时复用。建模以领域事件为核心驱动,自顶向下逐步推导出命令、聚合、读模型,每一步都有明确的原则与产出物。
本文既是方法论参考,也是可执行的工作流:先确定模块(见下),再按"建模流程"的阶段顺序推进,每阶段遵循其原则、产出对应文档,并在"关键判据与抉择"处做必要的决策。
适用场景
- 为一个新业务领域(或子系统)做完整的领域建模。
- 修订/补全现有领域模型(如新增业务流程后,排查事件、命令、聚合、读模型是否需要调整)。
- 对已有需求文档做领域设计落地。
建模前置:确定模块
每次建模只针对一个模块。 开始前必须先确定当前在为哪个模块建模:
- 若用户已提供模块名,直接采用。
- 若用户未提供,根据需求语义设计一个 kebab-case 英文名(小写字母 + 连字符),如
order-fulfillment、redemption-activity。 - 一个项目的第一个模块默认命名为
main。
确定模块名 <module-name> 后,本次建模的所有产出文档放在项目根的 ddd-modeling/<module-name>/ 目录下。
单模块原则
- 一次建模只处理一个模块,不跨模块混建。
- 建模过程中可以参考其他已有模块的文档(如复用已定义的端口、对齐值对象与命名约定、避免聚合/事件重名),但当前产出只写入当前模块目录。
- 若发现当前模块需要调用另一模块的能力,以端口形式声明依赖(定义于本模块 api 包,由外部实现),不要把对方模块的内部设计搬进来。
示例:本项目首个模块
main的产出位于ddd-modeling/main/。
建模流程总览
以下各阶段产出的文档均位于 ddd-modeling/<module-name>/ 目录下(<module-name> 在上文"建模前置"确定);流程图中仅标注文件名。
1. 需求基础 → requirements.md
2. 用例分析 → usecases.md
3. 领域事件 → domain-events.md
4. 领域命令 → domain-commands.md
5. 领域事件监听器排查 → domain-event-listeners.md
6. 读模型设计 → query-readmodels.md(必要时回头调整事件/命令)
7. 索引完整性排查 → 修订 domain-events.md / domain-commands.md
8. 聚合设计 → domain-aggregates.md
9. 聚合字段设计 → 修订 domain-aggregates.md
10. 性能排查 → 修订 domain-aggregates.md / domain-commands.md原则:顺序推进,后一步可能触发对前一步的回溯修订(如读模型发现事件信息不足,要回头补事件)。遇到需要用户定夺的架构抉择时,暂停并请示。
阶段 1:需求基础
目的:把业务梳理成结构化的流程与规则,作为后续建模的依据。
产出:requirements.md,含概述、角色、业务流程(含状态机)、业务规则。
原则
- 业务流程要画清状态机(特别是有生命周期的实体,如活动、订单)。
- 业务规则要明确边界条件(如"下架不影响兑换"、"停止 2 个月内可恢复")。
- 聚焦"业务是什么、规则是什么",先不纠结如何拆分用例或命令。
阶段 2:用例分析
目的:把需求转化为结构化的用例,作为领域命令设计的直接依据。
产出:usecases.md,用例(UC)列表,每个用例含:编号、名称、参与者、前置条件、主流程、后置条件、业务规则。
原则
- 每个用例对应一个明确的业务目标,后续通常映射为一个领域命令。
- 区分"命令式用例"(改变状态,将设计为命令)与"查询用例"(只读,不设计为命令,归读模型)。
- 备选流程、业务规则要写清,它们将转化为命令的校验规则与参考事件。
阶段 3:领域事件设计
目的:用事件刻画业务过程中"已经发生的事实"。
产出:domain-events.md,按业务域分组,每个事件含:名称、业务含义描述、数据字段表(字段名/类型/说明)、事件总览表。
原则
- 事件表达已完成的业务事实,用过去时态描述("xx 已创建")。
- 英文编码以
Evt结尾(如SkuCreatedEvt)。 - 每个事件隐含公共元数据:事件 ID、发生时间、所属聚合 ID(这是后续聚合划分的依据)。
- 事件必须不可变。
- 事件携带的是"业务事实数据",它可以包含不进入聚合状态的字段(如命令传入的参数),用于记录事实、喂养读模型。
- 先设计事件,不要急于设计聚合——事件是推导聚合的输入。
阶段 4:领域命令设计
目的:定义领域外部向领域发起的、期望达成某业务目标的指令。
产出:domain-commands.md,每个命令含:英文编码、业务含义、携带数据、校验规则、发出事件、参考的领域事件、命令总览、命令与用例映射。
原则
- 命令表达要达成的业务目的,英文编码以
Cmd结尾。 - "参考的领域事件"列明执行该命令时需查找哪些过去事件来支撑业务校验——这是后续索引排查的输入。
- 查询类操作(列表、详情)不产生状态变更,不设计为命令,归到读模型。
- 命令携带的数据 = 执行校验与发出事件所需的输入。
阶段 5:领域事件监听器排查
目的:识别"反应式业务规则"——「当 X 事件发生时,若满足 Y 条件,则执行 Z 领域命令」。这类规则用领域事件监听器实现,可减少后续聚合间的直接调用依赖。
为何紧随领域命令之后:判断是否需要监听器,只需已知"有哪些事件、有哪些命令"。识别出的监听器(事件→命令的触发关系)将作为既有的触发点,在后续聚合设计、索引排查中纳入。
方法:对每个领域事件确认"发生后是否需自动触发某领域命令"。
容易误判为监听器、实则不是的场景
- 时间触发的自动动作(如到期自动开始/停止):由定时任务轮询驱动,不是事件监听。
- 派生状态(如"超期变终态"):由命令校验被动拦截 + 读模型派生展示,无需主动转换命令。
- 跨聚合强一致编排(如兑换同时改多个聚合):由命令处理器同事务完成(聚合设计阶段处理),不用异步监听器。
产出:domain-event-listeners.md。若排查后确认无反应式业务,如实记录结论与逐事件排查依据,并预留未来扩展位置。
阶段 6:读模型设计
目的:为展示与复杂查询设计读模型。读模型完全由领域事件喂养生成,承载所有面向外部的展示数据与查询能力。
读模型的两个作用
- 对外展示:给系统外部的人 / 别的系统看的数据。
- 复杂查询:实现难以通过查聚合完成的查询(按非主键筛选的列表、统计计数等)。
为何紧随领域事件监听器排查之后:读模型由领域事件驱动,此时事件已设计完成,可立即推导读模型。更重要的是,读模型设计能及早暴露"事件信息不足"——若某展示/查询字段无法从事件获得,必须回头调整领域事件(必要时连锁调整命令);越早发现,回溯成本越低。
核心约束:每个读模型必须能通过监听领域事件完整生成与更新。
每个读模型定义:名称、用途、数据字段、数据索引(及支持的查询)、监听的事件及生成/修改动作。
与聚合字段设计的关系:读模型承载所有展示/查询数据;后续阶段 9(聚合字段设计)据此确认聚合只持有参与业务逻辑的字段,其余展示数据一律由读模型承接——两者共同形成读写分离。
产出:query-readmodels.md,含读模型总览、各读模型详述、事件到读模型的喂养映射矩阵。
阶段 7:命令/事件 id 与索引完整性排查
目的:确保每个命令在执行时,都能定位到其"参考的领域事件",使业务逻辑完整可执行。
方法
- 对每个命令的每条"参考的领域事件",核对:命令(或其执行中已查到的事件)是否携带了足够定位该事件的聚合 id 或索引条件。
- 若定位链路断裂(如命令输入是业务键 A,但要查的事件以聚合 id B 索引,且无法从 A 得到 B),则需补全:
- 给命令/事件补充 id 或索引字段;
- 必要时新增领域事件(典型场景:某聚合缺少"创建"事件,导致无法确认其存在、无法从业务键反查聚合 id)。
- 核心信条:命令执行的业务信息,只来自当前命令或过去领域事件。
产出:修订 domain-events.md / domain-commands.md,并给出排查结论。
阶段 8:聚合设计
目的:把事件归属到聚合,把命令分解为可执行的子功能。
方法与原则
- 按聚合 ID 归属事件:携带相同聚合 ID 的事件归属同一聚合,给聚合取确切有意义的名字。
- 命令分解为子功能:每个命令的功能分解为一到多个子功能;子功能要么由聚合(含 Factory)实现,要么由抽象端口(领域服务)实现,端口定义在领域模块 api 包、由模块外实现。多数命令恰好由一个聚合实现。
- 命令携带定位信息:命令须携带聚合 id 或索引条件,以便找到承载子功能的聚合。逐命令核对定位能力。
- 跨聚合编排:一个命令需原子地改动多个聚合时,由命令处理器(领域模块内)在同一事务内加载各聚合、调用端口、编排子功能。这不构成"聚合间直接依赖"。
- 端口:跨聚合的批量校验、生成等能力(无法通过加载单个聚合完成)抽象为端口。若依赖的是另一模块的能力,同样以端口声明依赖。
产出:domain-aggregates.md,含聚合总览、各聚合(id、状态、不变量、子功能方法表)、端口表、命令到子功能分解表、命令定位能力校验。
阶段 9:聚合字段设计
目的:精确定义每个聚合持有的数据字段,剔除不参与业务逻辑的冗余字段。
字段归属判据(严格) 聚合只持有参与业务逻辑的字段,即满足下列之一:
- 被聚合内某方法的业务校验 / 状态判断所读取;
- 被外部聚合 / 命令加载后用于其业务逻辑。
下列字段不归属聚合,移至读模型:
- 仅被写入覆盖、不被读取判断的字段;
- 仅供查询展示的字段。
方法:对每个字段溯源其使用依据(被哪个方法的什么逻辑读取,或被哪个命令加载用于什么业务)。无依据则剔除。
注意
- 同一类数据在不同聚合可能归属不同:如 A 聚合对其做校验则保留,B 聚合不校验则移出——这是判据一致应用的结果,不是矛盾。
- 移出聚合的字段仍可由领域事件携带(记录事实、喂养读模型),事件不必与聚合状态字段完全一致。
- 移出字段后,相关命令的聚合方法签名仍可接收这些值(用于发出事件),但不写入聚合状态。
- 移出聚合的展示字段由阶段 6 的读模型承接(读模型已设计)。
产出:domain-aggregates.md 各聚合的"聚合状态字段(含使用依据)"+"移出聚合的字段"+字段归属汇总表。
阶段 10:聚合性能排查
目的:聚合往往被整体载入内存、整体持久化,需评估持有数据量,避免内存与 IO 开销过大。
方法
- 逐聚合列出持久状态字段,识别其中的集合字段。
- 评估集合字段的增长边界(业务上是否有界)。
- 评估聚合是否需要"整体载入全部数据"——若某操作只需部分数据却被迫整体载入,考虑增量载入。
- 出现性能风险时的对策:拆分成更小聚合;或标记该聚合采用增量载入/更新策略(若可行)。
- 同时排查"单次批量操作的规模"(如批量生成大量子聚合),用命令校验限定单次数量上限。
产出:排查结论,必要时修订聚合结构或在命令校验中加入数量约束。
关键判据与抉择
建模中会反复遇到以下抉择,需明确立场或在必要时请示用户:
| 抉择点 | 立场 |
| 字段归属判据 | 默认采用严格判据(只保留参与业务逻辑的字段),展示字段移至读模型 |
| 跨聚合强一致编排 | 用命令处理器同事务编排,不用异步监听器 |
| 跨聚合查询/生成能力 | 抽象为端口,由领域模块外实现 |
| 读模型字段无法由事件生成 | 回头调整事件(乃至聚合/命令),而非放弃该读模型字段 |
| 历史数据是否保快照 | 视业务而定(如订单需保下单时 sku 快照,则事件须携带快照) |
| 是否需要领域事件监听器 | 仅当存在真正的"事件触发命令"反应式业务时才设计;否则如实记录"无需" |
| 模块边界 | 一次只建一个模块;跨模块依赖用端口声明,不混建 |
遇到无法自行定夺的架构抉择(如订单 sku 用快照还是实时关联、聚合边界如何拆分、是否需拆分新模块),暂停并请示用户,给出选项与利弊,再继续。
产出文档清单
完成完整建模后,ddd-modeling/<module-name>/ 下应有:
| 文档 | 内容 |
requirements.md | 需求、角色、业务流程、业务规则 |
usecases.md | 用例设计 |
domain-events.md | 领域事件(按域分组 + 事件总览) |
domain-commands.md | 领域命令(含参考事件 + 命令总览 + 用例映射) |
domain-event-listeners.md | 领域事件监听器(反应式业务排查结论) |
query-readmodels.md | 读模型(字段 + 索引 + 事件喂养映射) |
domain-aggregates.md | 聚合(字段归属 + 子功能 + 端口 + 命令分解) |
命名与风格约定
- 领域事件:
XxxEvt;领域命令:XxxCmd;值对象用名词(如SkuSnapshot)。 - 端口:以
Port结尾(如SkuQueryPort、CodeGenerationPort),定义于领域模块 api 包。 - 聚合方法:Factory 用
create,状态变更用动词(如start/pause/ship)。 - 文档用中文,语言简洁、精确、严肃;表格优先用于结构化对照。
注意事项
- 顺序不是绝对线性的:后续阶段常触发对前序文档的回溯修订,保持各文档一致。
- 一致性校验:每次修订后,用检索核对术语/标识符(如
skuIdsvsskuSnapshots、端口名)在所有文档中一致。 - 遇阻即请示:遇到设计层面无法自行定夺的抉择、或需求模糊处,暂停请示用户,不要猜测。
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

