小区要办美食节,大家在微信群里聊得热火朝天,却越聊越乱:有人说报名,有人说采购,有人说场地,信息全堵在聊天记录里。王阿姨提议:「别在手机里吵了,去活动室,把整件事贴到墙上。」于是大家拿不同颜色的便签,从第一件发生的事开始,一张张往回推:报名截止、摊位开张、菜品售罄……不到半天,整场美食节从头到尾的流程清清楚楚摆在了墙上。 这个「在墙上一起重演业务」的方法,就是事件风暴(Event Storming)。它不是画架构图,而是一场用便签、白墙和对话完成的协作游戏:业务专家贴上事实,技术人员追问细节,所有人围绕一张张便签达成共识。你不需要先懂 DDD,只需要像策划美食节一样,把复杂业务拆成一连串「已经发生的事」,再顺着这串事找出是谁做了什么、规则是什么、边界在哪里。

岗位关联

对 AI 从业者来说,事件风暴是进入复杂业务时的「破冰锤」:它帮你快速建立领域地图,让 AI 模型、微服务或智能系统的边界不再靠猜,而是靠整个团队一起贴出来。

目录
  1. 可视化演示
  2. 认识事件风暴
  3. 工作坊准备
  4. 领域事件收集
  5. 命令与角色
  6. 聚合设计
  7. 限界上下文
  8. 划分微服务
  9. 实操与落地
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

展示事件风暴工作坊从Big Picture到Design Level再到微服务边界划分的完整流程,切换模式查看各阶段核心方法。

本节配有交互动效(进入视口后自动加载,约 3KB)。

Big Picture事件时间线 橙色便利贴代表领域事件,按时间从左到右排列形成端到端业务流程 Event → Timeline → Domain Map 从业务起点出发,逐步探索所有领域事件

认识事件风暴

小区要办美食节,大家在微信群里聊得热火朝天,却越聊越乱:有人说报名,有人说采购,有人说场地,信息全堵在聊天记录里。王阿姨提议:「别在手机里吵了,去活动室,把整件事贴到墙上。」于是大家拿不同颜色的便签,从第一件发生的事开始,一张张往回推:报名截止、摊位开张、菜品售罄……不到半天,整场美食节从头到尾的流程清清楚楚摆在了墙上。

这个「在墙上一起重演业务」的方法,就是事件风暴。它由 Alberto Brandolini 提出,本质上不是画图考试,而是一场把领域知识从每个人脑子里搬到墙上的协作仪式。它有两种粒度:Big Picture 像看整张活动安排表,帮团队建立全局视野;Design Level 像专门设计某个摊位怎么运转,用于深入建模。无论哪种粒度,核心动作都是「贴便签、排顺序、讲理由」。

工作坊里的便签颜色不是装饰,而是共同语言:橙色写「已经发生的事」——领域事件;蓝色写「想做的事」——命令;紫色写参与者;绿色写业务规则;黄色圈出聚合;粉色标出痛点。颜色让人群不用开口解释,就能读懂彼此的意图。

就像美食节最后赢得的不是墙上那张时间线,而是大家终于敢说「原来整件事是这样的」——事件风暴的收获,正是这份共识。

Event Storming:参与者×便签×时间线⟶共享领域模型\displaystyle \text{Event Storming}: \text{参与者} \times \text{便签} \times \text{时间线} \longrightarrow \text{共享领域模型}
这个式子不是数学定理,而是提醒我们:人、道具、流程三者都齐了,领域模型才会从墙上长出来。
  1. 参与者是燃料领域专家提供业务事实,技术人员把握实现边界,缺了任何一方模型都会失真。
    参与者∈{业务专家,技术专家}\displaystyle \text{参与者} \in \{\text{业务专家}, \text{技术专家}\}
  2. 便签是共同语言每种颜色对应一种建模元素,让复杂概念可以被看见、被移动。
    便签={事件,命令,角色,规则}\displaystyle \text{便签} = \{\text{事件}, \text{命令}, \text{角色}, \text{规则}\}
  3. 时间线是骨架所有事件沿时间流动,模型的顺序感和因果关系由此而来。
    时间线=(e1,e2,…,en)\displaystyle \text{时间线} = (e_1, e_2, \dots, e_n)
  4. 模型是共识最终产物不是一张完美图纸,而是团队对业务的一致理解。
    共享领域模型≠代码\displaystyle \text{共享领域模型} \neq \text{代码}

工作坊准备

办美食节前,王阿姨会先做三件事:定场地、叫人、想清楚「这次到底解决什么问题」。事件风暴也一样,准备工作决定成败。

首先是场地。你需要一面足够长的白墙,至少能铺开一两个小时演进出来的事件流。用美纹纸贴出时间线,准备好大号便签和粗头马克笔。空间最好大到能让 8 到 15 人同时站着围观,而不是挤在会议室里轮流看投影。

其次是人员。业务专家、产品经理、开发、测试、架构师、运维,甚至财务和客服都可以来。不要只叫技术团队,否则你贴出来的是技术团队脑补的业务。引导师只负责提问题、控节奏,不负责给答案。

最后是聚焦问题。今天要回答的是什么?「新业务怎么上线」「遗留系统哪里最痛」「AI 客服为什么总答非所问」都可以。问题越具体,贴纸风暴的落点越准。

准备阶段就像选锅具和买食材:锅不够大,再好的菜谱也施展不开;人没到齐,最后端上桌的菜一定不是大家想吃的。

Workshop=⟨Goal,People,Space,Colors⟩\displaystyle \text{Workshop} = \langle \text{Goal}, \text{People}, \text{Space}, \text{Colors} \rangle
一场工作坊可看成四元组:目标、人、场地、颜色。四者齐备,讨论才顺畅。
  1. 目标是方向盘没有明确问题,便签会贴到天花板上。
    Goal∈{问题陈述,产出期望}\displaystyle \text{Goal} \in \{\text{问题陈述}, \text{产出期望}\}
  2. 人是建模主体业务侧讲“为什么”,技术侧讲“怎么做”,两者互补。
    People={业务代表,技术人员,引导师}\displaystyle \text{People} = \{\text{业务代表}, \text{技术人员}, \text{引导师}\}
  3. 场地是画布长墙优于白板,因为一屏装不下复杂业务的全貌。
    Space≥可以站下所有参与者的墙面\displaystyle \text{Space} \geq \text{可以站下所有参与者的墙面}
  4. 颜色是符号系统提前约定色码,讨论时不用反复解释。
    Colors={事件,命令,角色,规则,聚合,热点}\displaystyle \text{Colors} = \{\text{事件},\text{命令},\text{角色},\text{规则},\text{聚合},\text{热点}\}

领域事件收集

美食节正式开始前,最怕的就是「我们不知道从哪一步开始」。事件风暴的解法很简单:先别管流程和系统,只收集「已经发生的事」。

领域事件是一个发生在过去的事实,比如「报名截止」「摊位开张」「菜品售罄」。它用过去时态写,因为它是业务世界里的完成态,不是待办,也不是猜想。写「报名截止」,不写「要截止报名」;写「菜品售罄」,不写「准备补货」。一旦写错时态,讨论就会从事实滑向幻想。

在这个阶段,大家做的是 Big Picture:把脑子里所有相关事件都扔到墙上,再按时间顺序从左到右排好。遇到不确定的地方就贴一张「热点」标记,红色或粉色便签都可以。这部分不是为了得到正确答案,而是为了把业务地图铺满整面墙,让所有人都看见全局。

贴完所有事件,王阿姨看着满墙的橙色便签笑了:「原来从报名到收摊,一共就这么多节点。」这个「原来如此」的时刻,就是 Big Picture 的价值。

Event Flow=(e1,e2,…,en),ei∈Domain Events,time(e1)≤time(e2)≤⋯\displaystyle \text{Event Flow} = (e_1, e_2, \dots, e_n), \quad e_i \in \text{Domain Events}, \quad \text{time}(e_1) \le \text{time}(e_2) \le \cdots
领域事件不是孤立的点,而是沿着时间线流动的故事。排序比罗列更重要。
  1. 事件是事实每个事件必须用过去时态描述,代表业务世界中已经完成的瞬间。
    ei=名词短语+过去时态\displaystyle e_i = \text{名词短语} + \text{过去时态}
  2. 按时间排列同一时间发生的并行事件,可以上下分行放。
    time(ei)≤time(ei+1)\displaystyle \text{time}(e_i) \le \text{time}(e_{i+1})
  3. 热点标记业务痛点、歧义点、争议点单独标出,后续深入分析。
    hot spot∈{痛点,疑问,规则冲突}\displaystyle \text{hot spot} \in \{\text{痛点}, \text{疑问}, \text{规则冲突}\}
  4. 全局图景事件墙完成后,团队第一次看到完整的业务全景。
    全景=⋃i=1n{ei}\displaystyle \text{全景} = \bigcup_{i=1}^{n} \{e_i\}

命令与角色

事件墙铺好了,接下来要问一个关键的「为什么」:这件事是谁干的?什么动作触发了它?

命令就是「想要做的事」,它表达的是业务意图,比如「提交报名表」「打开摊棚」「通知补货」。命令通常由某个角色发起:摊主、志愿者、管理员。没有角色,命令就像没有主人的便利贴;没有命令,事件就像凭空掉下来的巧合。

事件风暴里最有趣的环节是沿着时间线来回追问。看到一个事件,往左问:「什么命令导致了它?」看到一个命令,往右问:「它会生成什么事件?」当一个事件成为下一轮业务的导火索,比如「菜品售罄」触发「通知补货」,事件和命令就形成了接力链。业务规则这时候也会冒出来:谁有权限发起命令?什么条件下命令被允许?这些规则用绿色便签贴在命令和事件之间。

在美食节现场,售罄的摊位一声吆喝,志愿者就去仓库补货。这个“吆喝”和“补货”之间的线,就是事件与命令之间最鲜活的业务链路。

Command→PolicyexecutesDomain Event,Domain Event⇝Command\displaystyle \text{Command} \xrightarrow[\text{Policy}]{\text{executes}} \text{Domain Event}, \quad \text{Domain Event} \rightsquigarrow \text{Command}
命令在业务规则的约束下产生事件,事件又会引发下一个命令。这就是业务流转的脉搏。
  1. 命令是触发者命令表达“我想让某件事发生”,是事件的源头。
    Command⟶Domain Event\displaystyle \text{Command} \longrightarrow \text{Domain Event}
  2. 角色是主语每条命令都要问清楚是谁发起的,避免责任悬空。
    Command=⟨Actor,Action⟩\displaystyle \text{Command} = \langle \text{Actor}, \text{Action} \rangle
  3. 规则是阀门业务规则决定命令能否通过,也决定规则冲突时的走向。
    Policy:Command→{0,1}\displaystyle \text{Policy}: \text{Command} \to \{0, 1\}
  4. 事件会反向触发一个领域事件可能成为下一个命令的触发器,形成涟漪。
    Domain Event⇝Command′\displaystyle \text{Domain Event} \rightsquigarrow \text{Command}'

聚合设计

现在把镜头推近,进入 Design Level。只见满墙便签已经排成几条故事线,但还不够:哪些便签应该被圈在一起,作为一个整体来修改?

「摊位」就是一个很好的例子。一个摊位的报名、开张、售罄都是围绕同一个身份发生的,它们必须一起被维护,不能只改一半。这个被圈出来的整体就是聚合(Aggregate)。聚合是一个一致性边界:边界内的事物要么一起成功,要么一起失败。边界外的事物通过聚合根引用,而不是直接改内部细节。

识别聚合时,可以问三个问题:哪些业务不变量必须同时满足?哪些便签共享同一个实体标识?哪组事件只要发生一次,就会带来一连串状态变化?回答完,用黄色便签或线圈把这些事件圈起来,聚合就出现了。聚合不是越大越好,也不是越小越好,而是刚好能装下一次业务操作所依赖的完整性。

摊位上所有东西都围在同一个棚子里:锅、灶、菜单、收款码。如果有人把菜单改成另一家摊位的,这个摊位就不再完整。聚合的边界,就像摊棚的边界一样,必须清晰且稳定。

Aggregate=⟨Entity,Invariants,Event Stream⟩\displaystyle \text{Aggregate} = \langle \text{Entity}, \text{Invariants}, \text{Event Stream} \rangle
聚合是携带不变量的实体边界:业务规则要求“一起变”的东西,必须待在同一个圈里。
  1. 实体是聚合根每个聚合有一个唯一标识,外界只能通过它来引用聚合。
    Aggregate Root∈Entity\displaystyle \text{Aggregate Root} \in \text{Entity}
  2. 不变量是生命线这些规则一旦被打破,业务就会出错,因此必须被边界保护。
    Invariants={必须始终成立的条件}\displaystyle \text{Invariants} = \{\text{必须始终成立的条件}\}
  3. 事件流是历史聚合产生的事件记录它的状态变化,也是与其他模块沟通的语言。
    Event Stream=(e1,e2,…,ek)\displaystyle \text{Event Stream} = (e_1, e_2, \dots, e_k)
  4. 范围是事务边界一次命令只需要且只能修改一个聚合。
    Transaction⊆One Aggregate\displaystyle \text{Transaction} \subseteq \text{One Aggregate}

限界上下文

美食节办到一半,王阿姨发现一个有意思的现象:同样叫「摊位」,在报名组眼里是一个编号,在餐饮检查组眼里是一个卫生责任人,在财务组眼里是一个收入主体。同一个词,在不同场景下含义不同。

这就是限界上下文(Bounded Context)。它把一个大型业务划分成多个语义边界,每个边界内使用自己的统一语言。在「报名上下文」里,一个摊位是候选摊主;在「经营上下文」里,一个摊位是正在营业的单元。强行让所有人共用同一个「摊位」概念,模型会变得臃肿且充满歧义。

事件风暴的 Big Picture 阶段会把不同的业务区域区分出来,并用上下文地图画出它们的关系:上游还是下游?共享内核还是客户-供应商?上下文之间的关系,决定了团队之间如何同步模型、如何协作,也决定了 API 和数据所有权。

美食节的报名组和财务组不需要共用同一本台账:报名组关心有没有人,财务组关心收了多少钱。各管各的语义,只在交界处对账,这就是限界上下文的智慧。

ContextMap={⟨Bi,Bj,Rij⟩∣Rij∈{upstream,downstream,shared kernel}}\displaystyle \text{ContextMap} = \{ \langle B_i, B_j, R_{ij} \rangle \mid R_{ij} \in \{\text{upstream}, \text{downstream}, \text{shared kernel}\} \}
限界上下文让“同一个词可以有不同意思”这件事变得合法,也让团队可以在各自边界内自由演进。
  1. 统一语言只住在上下文内每个上下文有自己的术语表,跨上下文就翻译。
    Ubiquitous Language⊆Bi\displaystyle \text{Ubiquitous Language} \subseteq B_i
  2. 上下文之间有映射关系上游模型影响下游,但下游不能反过来污染上游。
    Rij:Bi→Bj\displaystyle R_{ij}: B_i \to B_j
  3. 子域对应业务能力核心子域、支撑子域、通用子域区分投入优先级。
    Subdomain∈{核心,支撑,通用}\displaystyle \text{Subdomain} \in \{\text{核心}, \text{支撑}, \text{通用}\}
  4. 边界影响组织边界上下文通常与团队、代码库、数据库所有权对齐。
    Bi↔Teami\displaystyle B_i \leftrightarrow \text{Team}_i

划分微服务

墙面上的上下文画好了,最后一个问题水到渠成:微服务从哪里切?

答案是:优先对齐限界上下文,而不是对齐每一个聚合或每一条命令。一个限界上下文往往对应一个完整业务能力,比如「报名管理」「现场经营」「资金结算」。一个服务内部可以包含多个聚合,只要它们都属于同一个业务上下文、共享同一套领域语言,并且需要一起演化。

事件风暴帮团队避免了两个极端:一是把所有东西塞进一个「大泥球」单体;二是把每个聚合都拆成微服务,结果服务数量爆炸、数据散落、改一个流程要跨十个系统。正确姿势是让服务拥有自己的数据,对外只暴露契约;上下文之间的交互通过事件或 API,而不是直接访问对方数据库。

把整个美食节切成「报名」「经营」「结算」三个岛,每个岛自己管自己的摊位数据,岛与岛之间只传轻量消息。这样既不会过度碎片化,也不会让一个岛包揽所有事。

Microservice≅Bounded Context⊇{Aggregates}\displaystyle \text{Microservice} \cong \text{Bounded Context} \supseteq \{\text{Aggregates}\}
微服务不是和每个聚合一一对应,而是和限界上下文对齐:一个上下文内部可以打包多个聚合。
  1. 服务是业务能力的载体先找业务能力,再找承载它的服务边界。
    Capability→Service\displaystyle \text{Capability} \to \text{Service}
  2. 上下文是服务的容器一个限界上下文通常对应一个可独立演化的服务或服务组。
    Service≈Bi\displaystyle \text{Service} \approx B_i
  3. 聚合是服务的内部单元多个聚合可以在服务内协作,但不跨服务做强一致事务。
    Service⊇{Aggregatej}\displaystyle \text{Service} \supseteq \{\text{Aggregate}_j\}
  4. 数据所有权决定边界谁拥有数据,谁才能修改数据,这是微服务不失控的底线。
    Data Owner=Service\displaystyle \text{Data Owner} = \text{Service}

实操与落地

知道方法还不够,关键是把工作坊开好。引导师是整个活动的节奏掌控者:他要不断提问「这个事件再往左一步是什么?」「这个规则谁定的?」同时还要保护参与者,避免争论变成人身攻击。

时间盒(Timebox)是引导师最趁手的工具。Big Picture 可以设置 90 分钟:前 20 分钟收集事件,30 分钟排时间线,20 分钟标热点,20 分钟暂停回顾。到点就停下,不要沉浸在细节里。Big Picture 结束并不代表模型完成,而是给下一轮 Design Level 留下待办清单:哪个上下文值得深入,哪个热点需要业务专家补充。

事件风暴鼓励多轮迭代。第一轮贴出骨架,散会后大家消化;第二轮补充命令、角色和规则;第三轮尝试圈聚合、画上下文。远程团队也不用担心,用在线白板代替墙面,用虚拟便签代替实体便签,照样能开。

美食节筹备不是一次会议就能定稿的:先出大纲,大家回去想一晚上,第二天再补细节。事件风暴也是如此,贴纸不是答案,对话和迭代才是。

Design⇝Feedback⇝Refine⇝Consensus\displaystyle \text{Design} \rightsquigarrow \text{Feedback} \rightsquigarrow \text{Refine} \rightsquigarrow \text{Consensus}
模型不是一次成型,而是经过设计、反馈、细化不断逼近共识的循环。
  1. 设计是起点行动派先贴出第一版,哪怕粗糙。
    Design0=第一版便签墙\displaystyle \text{Design}_0 = \text{第一版便签墙}
  2. 反馈是修正力参与者指出错误、补充遗漏,模型开始生长。
    Feedback→Change Set\displaystyle \text{Feedback} \to \text{Change Set}
  3. 细化是收敛每一轮迭代都在缩小不确定性,最终收敛到共识。
    Refine⊆Design\displaystyle \text{Refine} \subseteq \text{Design}
  4. 共识是终点墙上的每张便签都有人能解释,才叫真正完成。
    Consensus=⋂参与者理解\displaystyle \text{Consensus} = \bigcap \text{参与者理解}

AI 应用场景

智能电商中台的领域建模

阶段:业务建模与需求澄清

解决的问题:电商团队常为「订单取消」后积分、库存、支付状态不一致而争执。传统方式靠文档和评审会,业务方和技术方各说各话,需求来回拉扯。用事件风暴先在墙上贴出订单全链路事件,再圈出聚合和上下文,AI 系统要感知的领域规则就清楚了。

具体案例:某电商中台团队用半天做 Big Picture,梳理出「订单已取消」「库存已释放」「积分已回滚」等事件,随后定位到库存上下文和订单上下文之间的边界,直接将三个服务的数据争议转化为契约设计。

银行信贷审批规则抽取

阶段:知识与规则抽取

解决的问题:信贷审批涉及征信查询、风控策略、人工复核等多种规则,规则分散在制度和老员工脑中。传统方式让分析师逐条访谈,效率低且易遗漏。事件风暴用「审批通过」「额度已调整」「申请被拒绝」等事件倒推出各环节命令和规则,为 AI 规则引擎提供结构化输入。

具体案例:某银行产品团队邀请风控、运营、研发共同参加两轮事件风暴,识别出「拒绝后 30 天内不允许重新申请」等 40 余条隐性规则,直接转化为决策引擎的规则集。

远程 AI 客服工作流梳理

阶段:跨团队协作设计

解决的问题:AI 客服涉及意图识别、订单查询、人工转接多个模块,模块边界不清导致模型训练语料和业务逻辑脱节。事件风暴可以在 Miro 等在线白板上进行,让异地团队同步梳理「用户问题已受理」「工单已创建」「转人工已触发」等事件,界定 AI 与人工的协作边界。

具体案例:某 SaaS 公司用远程事件风暴工作坊,用蓝便签收集命令、红便签收集事件,最终划分出「自助服务」和「人工服务」两个限界上下文,AI 客服的意图分类范围因此大幅收敛。

Python 演示

python67 行
from dataclasses import dataclass  @dataclassclass DomainEvent:    name: str    aggregate_id: str    position: int  @dataclassclass Command:    name: str  class EventStormingBoard:    def __init__(self, title):        self.title = title        self.event_list = []        self.command_list = []        self.policy_list = []     def add_event(self, name, aggregate_id):        event = DomainEvent(name, aggregate_id, len(self.event_list))        self.event_list.append(event)        return event     def add_command(self, name):        command = Command(name)        self.command_list.append(command)        return command     def add_policy(self, rule):        self.policy_list.append(rule)     def aggregate_map(self):        mapping = {}        for event in self.event_list:            mapping.setdefault(event.aggregate_id, []).append(event.name)        return mapping     def show_timeline(self):        for event in sorted(self.event_list, key=lambda e: e.position):            print(f"[event] {event.name} -> {event.aggregate_id}")        for command in self.command_list:            print(f"[command] {command.name}")        for policy in self.policy_list:            print(f"[policy] {policy}")  def main():    board = EventStormingBoard("Community Food Festival")    board.add_event("EnrollmentClosed", "Stall")    board.add_event("StallOpened", "Stall")    board.add_event("FoodSoldOut", "Stall")    board.add_event("TrashCleared", "Venue")    board.add_command("SubmitEnrollmentForm")    board.add_command("OpenStall")    board.add_command("NotifyRestock")    board.add_policy("Max three people per stall")    board.show_timeline()    for agg, evs in board.aggregate_map().items():        print(f"aggregate[{agg}]: {', '.join(evs)}")  if __name__ == "__main__":    main()
目的:用一个小型 Python 程序模拟事件风暴工作坊的便签墙,把领域事件、命令、业务规则和聚合分组打印出来。
  1. board.add_event("EnrollmentClosed", "Stall")像在工作坊中贴一张橙色便签,记录「报名截止」这个已经发生的事实。
  2. board.show_timeline()把便签按加入顺序输出,模拟事件墙上的时间线。
  3. board.aggregate_map()按 aggregate_id 把事件归组,模拟用线圈圈出聚合边界。
  4. mapping.setdefault(event.aggregate_id, []).append(event.name)用聚合 ID 做分组键,把属于同一个聚合的事件收集到同一列表。

常见误区

误区事件风暴就是把业务用例画成一张大图,和 UML 用例图差不多,最后产出一张“流程图”就够了。

正解事件风暴不是一场画图比赛,而是一次协作仪式。它的真正产出不是图纸,而是参与者之间达成的理解。你可以在墙上看到事件流,但那只是讨论的副产品;如果团队没有通过对话对齐认知,贴得再整齐的便签墙也只是装饰。

为何常见:大家习惯了开会要留下“文档”或“图”,于是把事件风暴误当成一种新的画图工具,忽略它的协作勘探本质。

误区只要把领域事件按时间线排好,照着事件的数量或前后顺序就能直接切出微服务边界。

正解领域事件只是建模的起点,之后还要分析命令、角色、业务规则、聚合、限界上下文,才能找到合理的服务边界。一个服务往往对应一个业务能力,而不是对应某一张事件便签。跳过这些步骤直接拆分,会得到一堆逻辑破碎的“分布式大泥球”。

为何常见:看到满墙事件便签会产生“模型已经完成”的错觉,忽略了事件背后的聚合和上下文才是边界的关键。

误区最好在一天内同时完成 Big Picture 和 Design Level,一次性得到微服务拆分方案。

正解Big Picture 和 Design Level 是不同粒度的活动:前者画全局,后者深入局部。全局图景可能就有几十个事件,每个热点都需要单独研讨。更现实的节奏是先开一场 Big Picture 找出关键上下文,再分多次 Design Level 逐步细化。

为何常见:业务方希望“一次会议就出结果”,技术方也低估了复杂业务的建模难度,结果把所有问题都塞进一个超长会议。

误区开发、测试自己关起门来贴便签就够了,业务专家不用参加,因为“业务文档已经写得很清楚”。

正解没有业务专家在场,事件、命令和规则全靠技术人员脑补,模型就会变成技术团队的自嗨产物。事件风暴的精髓就是让业务和技术共享同一面墙,用便签把模糊的领域知识逼出来。业务专家的缺席会让最重要的信息源断掉。

为何常见:业务专家往往很忙,技术人员又觉得“我早懂业务”,于是用猜测代替求证。

面试题

Q1. 在事件风暴工作坊中,哪种便签通常用来记录“已经发生的事情”?

单选题 · easy

  • A.橙色便签记录领域事件← 正确答案
  • B.蓝色便签记录触发命令
  • C.紫色便签记录参与角色
  • D.绿色便签记录业务规则

答案:A。领域事件是已经发生的事实,标准做法用橙色便签记录。蓝色对应命令,紫色对应角色,绿色对应规则。

Q2. 谁提出了事件风暴(Event Storming)方法?

单选题 · easy

  • A.Martin Fowler 提出事件风暴
  • B.Alberto Brandolini 提出事件风暴← 正确答案
  • C.Eric Evans 提出事件风暴
  • D.Kent Beck 提出事件风暴

答案:B。事件风暴由 Alberto Brandolini 提出,是一种协作式领域建模工作坊方法。Eric Evans 是 DDD 奠基人,但不是事件风暴的提出者。

Q3. Big Picture 阶段的主要目标是什么?

单选题 · easy

  • A.梳理全局流程并标出热点← 正确答案
  • B.编写微服务详细接口
  • C.设计数据库物理模型
  • D.定义服务器部署策略

答案:A。Big Picture 阶段用领域事件勾勒全局业务全景,并标记热点、痛点和争议点,为后续设计级建模划定范围。

Q4. 关于聚合的边界,下列说法正确的是?

单选题 · mid

  • A.聚合边界必须跨越多个限界上下文
  • B.聚合边界跟随业务一致性← 正确答案
  • C.聚合边界等于数据库表数量
  • D.聚合边界越小系统就越健壮

答案:B。聚合是业务一致性边界,边界内的事物必须一起满足业务不变量。它不是按数据表划分的,也不是越小越好。

Q5. 命令与领域事件的关系是什么?

单选题 · mid

  • A.命令执行后会产生领域事件← 正确答案
  • B.命令总在事件发生之后出现
  • C.领域事件只能由外部系统产生
  • D.命令和事件完全一一对应

答案:A。命令表达业务意图,执行业务操作后通常会产出领域事件。事件也可能触发新的命令,但二者并非总是严格一一对应。

Q6. 限界上下文的核心价值是什么?

单选题 · mid

  • A.让每个上下文内保持统一语言← 正确答案
  • B.让所有服务共享同一个领域模型
  • C.让数据库可以跨服务直接访问
  • D.让团队完全忽略业务边界

答案:A。限界上下文为模型划定语义边界,每个上下文内有自己的统一语言。不同上下文之间通过契约交互,而不是共享同一个模型。

Q7. 关于聚合与微服务拆分,最合理的做法是?

单选题 · hard

  • A.每个聚合都拆成独立微服务
  • B.按限界上下文组合相关聚合← 正确答案
  • C.微服务数量必须等于聚合数量
  • D.一个微服务内只能包含一个聚合

答案:B。微服务应优先对齐限界上下文和业务能力,一个上下文内可以包含多个聚合。把每个聚合都拆成服务会造成服务碎片化。

Q8. 事件风暴工作坊通常需要哪些角色参与?

多选题 · easy

  • A.业务领域专家← 正确答案
  • B.技术开发人员← 正确答案
  • C.产品与运营同事← 正确答案
  • D.外部审计师

答案:ABC。事件风暴需要业务侧提供领域知识,技术侧评估可行性,产品与运营帮助澄清需求。外部审计师不是常规参与者。

Q9. 哪些线索有助于识别聚合边界?

多选题 · mid

  • A.事务一致性要求← 正确答案
  • B.相同聚合根标识← 正确答案
  • C.业务不变量约束← 正确答案
  • D.前端页面组件数量

答案:ABC。聚合需要保证一致性并围绕聚合根组织,因此事务一致性和业务不变量是重要信号。前端组件数量与领域一致性无关。

Q10. 设计级事件风暴相比全局级,通常新增哪些内容?

多选题 · hard

  • A.命令、角色和业务规则← 正确答案
  • B.聚合与一致性边界← 正确答案
  • C.业务热点与争议点← 正确答案
  • D.云服务器监控告警规则

答案:ABC。设计级在全局事件流基础上深入分析命令、角色、规则、聚合和热点。基础设施监控不属于领域建模范围。