关于 event-modeling
event-modeling 是一项技能,用于在实施前引导结构化的领域建模讨论。它带领人类和 Agent 完成领域探索与工作流设计,产出事件模型、切片及 Given/When/Then 场景,并检查模型完整性。
主要功能
- 01引导领域探索和工作流设计
- 02建模参与者、事件、命令和读模型
- 03定义自动化与转换模式
- 04生成切片级 GWT 场景
- 05检查模型完整性与依赖关系
适用场景
- 01编码前建立共同理解
- 02将业务工作流记录为事件模型
- 03编写切片验收场景
- 04发现事件和读模型覆盖缺口
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jwilger/ agent-skills --skill event-modeling安装后在 Claude Code 中输入/event-modeling确认已加载。
附带文件references/gwt-template.mdreferences/nine-steps.md
详细介绍
事件建模
价值: 沟通——事件建模是一种结构化对话,能在编写任何代码之前,揭示隐藏的领域知识,并在人员与 Agent 之间建立共同理解。
目的
指导 Agent 按照 Martin Dilger 的“Understanding Eventsourcing”方法主持事件建模会议。产出完整的事件模型(参与者、事件、命令、读取模型、自动化、切片),为所有后续实现提供依据。模型位于
docs/event_model/。
实践
两阶段流程:先探索,再设计
在充分理解领域之前,切勿直接开始设计详细工作流。第一阶段绘制领域全貌;第二阶段逐一探索各个部分。
第一阶段——领域探索。 了解业务内容、参与者、主要流程、需要集成的外部系统,以及要建模的工作流。向用户询问这些问题;不要自行假设答案。输出:docs/event_model/domain/overview.md。
第二阶段——工作流设计。 对每个工作流,遵循 9 个步骤。必须遵循 references/nine-steps.md 中的完整方法。一次设计一个工作流。完成全部 9 个步骤后,再开始下一个工作流。输出:docs/event_model/workflows/<name>/overview.md,以及 slices/ 中的各个切片文件。
首要原则:不丢失信息
记录发生过什么(事件),而不仅仅记录当前状态。事件是以业务语言表述的、不可变的过去事实。每个读取模型字段都必须能追溯到某个事件。如果某个字段没有来源事件,说明模型有所遗漏。
事件设计规则
- 使用过去时态和业务语言命名事件:
OrderPlaced,而不是PlaceOrder或CreateOrderDTO - 事件是不可变事实——绝不修改或删除
- 包含相关数据:发生了什么、何时发生、由谁或什么导致
- 选择恰当的粒度——不要过于笼统,如
DataUpdated;也不要过于细致,如FieldXChanged - 命令依赖用户输入和事件流,而非读取模型。 读取模型仅用于视图和自动化。
- 事件记录领域事实(在任何机器上都成立)。运行时上下文 (文件路径、主机名、PID、工作目录)不应包含在事件数据中。
四种模式
每个事件溯源系统都使用这些模式。每种模式对应一个垂直切片。
- 状态变更: 命令 -> 事件。这是修改状态的唯一方式。单个操作中的一条 命令可以产生多个事件。
- 状态视图: 事件 -> 读取模型。系统通过这种方式响应查询。
当领域支持并发实例时,读取模型字段应使用集合类型,而非单值。
命令从用户提供的数据和事件流中获取输入,绝不能从读取模型中获取。
图中不应出现
ReadModel → Command边。如果命令需要检查某件事是否已经发生(例如,幂等性),它应检查事件流,而非读取模型。 读取模型表示有意义的领域投影。隐含在命令执行上下文中的基础设施前置条件(“目录是否存在?”、“服务是否正在运行?”)不需要单独的读取模型。 - 自动化: 事件 -> 读取模型(待办列表)-> 流程 -> 命令 -> 事件。由事件触发的后台工作。必须包含全部四个组件:触发事件、被查询的读取模型、有条件的流程逻辑,以及由此产生的命令。如果没有读取模型和条件逻辑,就不是自动化,而是产生多个事件的命令。必须有明确的终止条件。
- 转换: 外部数据 -> 内部事件。用于特定工作流外部集成的防腐层。所有工作流共用的通用基础设施(事件持久化、消息传输)不是转换,而是属于事件模型之外的横切基础设施。
每种 Slice 模式所需的层
每种 Slice 模式都隐含一组最低限度的架构层。只有实现并连接所有必需层后,Slice 才算完成。
- 状态视图:基础设施(从存储中读取事件/数据)+ 领域(投影/查询逻辑)+ 表现层(呈现结果或将结果返回给调用方)+ 应用接线(端到端连接各层)
- 状态变更:表现层(接收用户输入或外部请求)+ 领域(命令验证和业务规则)+ 基础设施(持久化生成的事件/数据)+ 应用接线(端到端连接各层)
- 自动化:基础设施(检测触发条件——计时器、外部事件、阈值)+ 领域(策略/决策逻辑)+ 基础设施(执行生成的操作——发送消息、写入数据、调用服务)+ 应用接线(连接触发条件、策略和操作)
- 转换:基础设施(从外部系统接收)+ 领域(映射/转换逻辑)+ 基础设施(将结果交付给目标系统)+ 应用接线(连接入站适配器、映射器和出站适配器)
拆解 Slice 时,请确认验收标准和任务分解涵盖每个必需层。只实现领域逻辑而不包含表现层或基础设施的 Slice 并不完整——它只是一个组件,而不是垂直 Slice。
GWT 场景
完成工作流设计后,为每个 Slice 生成 Given/When/Then 场景。 这些场景将成为实现的验收标准。
命令场景: Given = 用于确立状态的先前事件。When = 包含具体数据的命令。Then = 生成的事件或错误(二者不可兼有)。
视图场景: Given = 当前投影状态。When = 一个新事件。Then = 生成的投影状态。视图不能拒绝事件。
关键区别: GWT 场景用于测试业务规则(依赖状态的策略),而不是数据验证(属于类型系统的格式/结构检查)。如果类型系统能让无效状态无法表示,那么它就不属于 GWT 场景。
你 MUST 使用 references/gwt-template.md 中的完整场景格式和示例。
应用边界验收场景
每个垂直 Slice MUST 至少包含一个在应用边界定义的 GWT 场景:
- Given:系统处于已知状态(先前事件、种子数据、配置)
- When:用户(或外部调用方)通过应用的外部接口进行交互——具体接口取决于项目(HTTP 端点、CLI 命令、消息队列消费者、UI 操作等)
- Then:结果可在同一边界观察到——响应、输出、渲染后的状态变化、发出的事件等。
如果在单元测试中完全通过调用内部函数就能满足某个 GWT 场景,那么它描述的是单元级规范,而不是 Slice 验收标准。Slice 验收标准必须测试从外部输入到可观察输出的完整路径。
验收测试策略
当应用边界可通过程序测试时——例如 HTTP 端点、CLI 输出解析、无头浏览器自动化、消息队列断言、API 契约测试等——编写自动化验收测试,从外部输入到可观察输出完整测试 GWT 场景。这些测试能快速提供反馈,并作为 Slice 行为的动态文档。
当无法实际进行自动化边界测试时(例如复杂的 GUI 交互、依赖硬件的行为、视觉/美学验证),请记录人工应手动验证的内容:要执行的具体步骤和预期的可观察结果。这份手动验证清单将成为 Slice 完成定义的一部分。
切片独立性
共享同一事件架构的切片彼此独立。事件架构是共享契约。命令切片通过断言生成的事件来测试;视图切片使用合成事件夹具进行测试。两者都不需要等对方先实现。不要在人为设置的切片之间建立依赖链。
模型验证
编写 GWT 场景后,验证模型是否完整:
- 每个读取模型字段都能追溯到某个事件
- 每个事件都有对应的触发命令、自动化或转换
- 每个命令都有记录在案的拒绝条件(业务规则)
- 每个自动化都有终止条件
- GWT 的 Given/When/Then 子句不引用未定义的元素
发现缺口时,请用户澄清,创建缺失的元素,然后重新验证。不得在仍有缺口时继续。
引导思维
你是引导者,而不是记录员。提出深入的问题。质疑假设。每个事件、每个命令、每个回答之后,都要不断追问“然后会发生什么?”。使用业务语言,不要使用技术术语。在事件建模期间,不要讨论数据库、API、框架或实现。唯一的例外是:注明必须使用的第三方集成及其用途。
应该:
- 按顺序完成所有步骤——这个过程有助于加深理解
- 不断追问“然后会发生什么?”
- 在所有示例和场景中使用具体、真实的数据
- 一次设计一个工作流
- 在继续之前确保信息完整
- 针对读取模型字段询问“这类数据能否同时存在多个?”
- 将自动化标记为自动化之前,确认其具备全部四个组成部分
不应该:
- 不要因为觉得已经了解得足够多就跳过步骤
- 不要在建模期间做出架构或实现决策
- 不要为数据验证编写 GWT 场景(使用类型系统)
- 不要同时设计多个工作流
- 不要在模型存在缺口时继续
强制执行说明
- 独立模式:建议性。Agent 按惯例遵循九步方法。
- 流水线模式:门控式。不完整的模型(缺少 GWT 场景、自动化未定义)会阻止切片分解。
硬性约束:
- 模型存在缺口时不得继续:
[RP]
约束
- “MUST follow nine-steps.md”:遵循九个步骤意味着执行每个步骤的具体活动,并产出该步骤规定的成果。这并不意味着阅读参考资料后声称“我遵循了其精神”。每个步骤都有明确的产出——请将其产出。
- “Do not design multiple workflows simultaneously”:这包括在工作流 1 的步骤尚未完成时,就开始对工作流 2 进行“探索”。探索就是设计。如果你正在收集未来工作流的信息,你就是在设计它。
- 引导与记录:引导意味着提出问题,帮助领域专家发现他们尚未表达的内容。它不意味着提出引导性问题,把对方引向你偏好的答案。判断标准是:专家的回答是否真的有可能让你感到意外?如果不能,那你就是在引导对方,而不是在引导探索。
验证
完成事件建模后,验证以下事项:
-
docs/event_model/domain/overview.md中包含领域概览,涵盖参与者、工作流、外部集成以及建议的起始工作流 - 每个设计好的工作流都有
docs/event_model/workflows/<name>/overview.md,且 9 个步骤全部完成 - 所有事件均使用过去时、业务语言,并表示不可变事实
- 每个读取模型字段都可追溯到源事件
- 每个事件都有触发方式(命令、自动化或转换)
- 自动化包含全部四个组成部分(事件、读取模型、条件逻辑、命令)
- 当领域支持并发实例时,读取模型字段使用集合类型
- 没有将横切基础设施建模为转换切片
- 每个切片都有 GWT 场景(内联于
docs/event_model/workflows/<name>/slices/*.md),并包含具体数据 - GWT 错误场景只测试业务规则,不测试数据验证
- 共享事件架构的切片可独立测试(没有人为设置的依赖链)
- 验证后,模型中没有遗漏
如果有任何标准未达成,请先回顾相关实践,再继续。
依赖项
此技能可独立使用。如需增强工作流,可集成以下内容:
- domain-modeling: 事件揭示领域类型(Email、Money、OrderStatus),由领域建模技能进一步完善
- tdd: 每个垂直切片对应一个 TDD 循环
- architecture-decisions: 事件模型为架构提供信息;事件建模期间不应编写 ADR
- task-management: 工作流对应史诗,切片对应任务
缺少依赖项?使用以下命令安装:
npx skills add jwilger/agent-skills --skill domain-modeling