learn.lianglianglee.com/专栏/领域驱动设计实践(完)/061 事件风暴与业务全景探索.md.html
2022-05-11 19:04:14 +08:00

939 lines
47 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>061 事件风暴与业务全景探索.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/领域驱动设计实践(完)/001 「战略篇」访谈 DDD 和微服务是什么关系?.md.html">001 「战略篇」访谈 DDD 和微服务是什么关系?.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/002 「战略篇」开篇词:领域驱动设计,重焕青春的设计经典.md.html">002 「战略篇」开篇词:领域驱动设计,重焕青春的设计经典.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/003 领域驱动设计概览.md.html">003 领域驱动设计概览.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/004 深入分析软件的复杂度.md.html">004 深入分析软件的复杂度.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/005 控制软件复杂度的原则.md.html">005 控制软件复杂度的原则.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/006 领域驱动设计对软件复杂度的应对(上).md.html">006 领域驱动设计对软件复杂度的应对(上).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/007 领域驱动设计对软件复杂度的应对(下).md.html">007 领域驱动设计对软件复杂度的应对(下).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/008 软件开发团队的沟通与协作.md.html">008 软件开发团队的沟通与协作.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/009 运用领域场景分析提炼领域知识(上).md.html">009 运用领域场景分析提炼领域知识(上).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/010 运用领域场景分析提炼领域知识(下).md.html">010 运用领域场景分析提炼领域知识(下).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/011 建立统一语言.md.html">011 建立统一语言.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/012 理解限界上下文.md.html">012 理解限界上下文.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/013 限界上下文的控制力(上).md.html">013 限界上下文的控制力(上).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/014 限界上下文的控制力(下).md.html">014 限界上下文的控制力(下).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/015 识别限界上下文(上).md.html">015 识别限界上下文(上).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/016 识别限界上下文(下).md.html">016 识别限界上下文(下).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/017 理解上下文映射.md.html">017 理解上下文映射.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/018 上下文映射的团队协作模式.md.html">018 上下文映射的团队协作模式.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/019 上下文映射的通信集成模式.md.html">019 上下文映射的通信集成模式.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/020 辨别限界上下文的协作关系(上).md.html">020 辨别限界上下文的协作关系(上).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/021 辨别限界上下文的协作关系(下).md.html">021 辨别限界上下文的协作关系(下).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/022 认识分层架构.md.html">022 认识分层架构.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/023 分层架构的演化.md.html">023 分层架构的演化.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/024 领域驱动架构的演进.md.html">024 领域驱动架构的演进.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/025 案例 层次的职责与协作关系(图文篇).md.html">025 案例 层次的职责与协作关系(图文篇).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/026 限界上下文与架构.md.html">026 限界上下文与架构.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/027 限界上下文对架构的影响.md.html">027 限界上下文对架构的影响.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/028 领域驱动设计的代码模型.md.html">028 领域驱动设计的代码模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/029 代码模型的架构决策.md.html">029 代码模型的架构决策.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/030 实践 先启阶段的需求分析.md.html">030 实践 先启阶段的需求分析.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/031 实践 先启阶段的领域场景分析(上).md.html">031 实践 先启阶段的领域场景分析(上).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/032 实践 先启阶段的领域场景分析(下).md.html">032 实践 先启阶段的领域场景分析(下).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/033 实践 识别限界上下文.md.html">033 实践 识别限界上下文.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/034 实践 确定限界上下文的协作关系.md.html">034 实践 确定限界上下文的协作关系.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/035 实践 EAS 的整体架构.md.html">035 实践 EAS 的整体架构.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/036 「战术篇」访谈DDD 能帮开发团队提高设计水平吗?.md.html">036 「战术篇」访谈DDD 能帮开发团队提高设计水平吗?.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/037 「战术篇」开篇词:领域驱动设计的不确定性.md.html">037 「战术篇」开篇词:领域驱动设计的不确定性.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/038 什么是模型.md.html">038 什么是模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/039 数据分析模型.md.html">039 数据分析模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/040 数据设计模型.md.html">040 数据设计模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/041 数据模型与对象模型.md.html">041 数据模型与对象模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/042 数据实现模型.md.html">042 数据实现模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/043 案例 培训管理系统.md.html">043 案例 培训管理系统.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/044 服务资源模型.md.html">044 服务资源模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/045 服务行为模型.md.html">045 服务行为模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/046 服务设计模型.md.html">046 服务设计模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/047 领域模型驱动设计.md.html">047 领域模型驱动设计.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/048 领域实现模型.md.html">048 领域实现模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/049 理解领域模型.md.html">049 理解领域模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/050 领域模型与结构范式.md.html">050 领域模型与结构范式.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/051 领域模型与对象范式(上).md.html">051 领域模型与对象范式(上).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/052 领域模型与对象范式(中).md.html">052 领域模型与对象范式(中).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/053 领域模型与对象范式(下).md.html">053 领域模型与对象范式(下).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/054 领域模型与函数范式.md.html">054 领域模型与函数范式.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/055 领域驱动分层架构与对象模型.md.html">055 领域驱动分层架构与对象模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/056 统一语言与领域分析模型.md.html">056 统一语言与领域分析模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/057 精炼领域分析模型.md.html">057 精炼领域分析模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/058 彩色 UML 与彩色建模.md.html">058 彩色 UML 与彩色建模.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/059 四色建模法.md.html">059 四色建模法.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/060 案例 订单核心流程的四色建模.md.html">060 案例 订单核心流程的四色建模.md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/领域驱动设计实践(完)/061 事件风暴与业务全景探索.md.html">061 事件风暴与业务全景探索.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/062 事件风暴与领域分析建模.md.html">062 事件风暴与领域分析建模.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/063 案例 订单核心流程的事件风暴.md.html">063 案例 订单核心流程的事件风暴.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/064 表达领域设计模型.md.html">064 表达领域设计模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/065 实体.md.html">065 实体.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/066 值对象.md.html">066 值对象.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/067 对象图与聚合.md.html">067 对象图与聚合.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/068 聚合设计原则.md.html">068 聚合设计原则.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/069 聚合之间的关系.md.html">069 聚合之间的关系.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/070 聚合的设计过程.md.html">070 聚合的设计过程.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/071 案例 培训领域模型的聚合设计.md.html">071 案例 培训领域模型的聚合设计.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/072 领域模型对象的生命周期-工厂.md.html">072 领域模型对象的生命周期-工厂.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/073 领域模型对象的生命周期-资源库.md.html">073 领域模型对象的生命周期-资源库.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/074 领域服务.md.html">074 领域服务.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/075 案例 领域设计模型的价值.md.html">075 案例 领域设计模型的价值.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/076 应用服务.md.html">076 应用服务.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/077 场景的设计驱动力.md.html">077 场景的设计驱动力.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/078 案例 薪资管理系统的场景驱动设计.md.html">078 案例 薪资管理系统的场景驱动设计.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/079 场景驱动设计与 DCI 模式.md.html">079 场景驱动设计与 DCI 模式.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/080 领域事件.md.html">080 领域事件.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/081 发布者—订阅者模式.md.html">081 发布者—订阅者模式.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/082 事件溯源模式.md.html">082 事件溯源模式.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/083 测试优先的领域实现建模.md.html">083 测试优先的领域实现建模.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/084 深入理解简单设计.md.html">084 深入理解简单设计.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/085 案例 薪资管理系统的测试驱动开发(上).md.html">085 案例 薪资管理系统的测试驱动开发(上).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/086 案例 薪资管理系统的测试驱动开发(下).md.html">086 案例 薪资管理系统的测试驱动开发(下).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/087 对象关系映射(上).md.html">087 对象关系映射(上).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/088 对象关系映射(下).md.html">088 对象关系映射(下).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/089 领域模型与数据模型.md.html">089 领域模型与数据模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/090 领域驱动设计对持久化的影响.md.html">090 领域驱动设计对持久化的影响.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/091 领域驱动设计体系.md.html">091 领域驱动设计体系.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/092 子领域与限界上下文.md.html">092 子领域与限界上下文.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/093 限界上下文的边界与协作.md.html">093 限界上下文的边界与协作.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/094 限界上下文之间的分布式通信.md.html">094 限界上下文之间的分布式通信.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/095 命令查询职责分离.md.html">095 命令查询职责分离.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/096 分布式柔性事务.md.html">096 分布式柔性事务.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/097 设计概念的统一语言.md.html">097 设计概念的统一语言.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/098 模型对象.md.html">098 模型对象.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/099 领域驱动设计参考过程模型.md.html">099 领域驱动设计参考过程模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/100 领域驱动设计的精髓.md.html">100 领域驱动设计的精髓.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/101 实践 员工上下文的领域建模.md.html">101 实践 员工上下文的领域建模.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/102 实践 考勤上下文的领域建模.md.html">102 实践 考勤上下文的领域建模.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/103 实践 项目上下文的领域建模.md.html">103 实践 项目上下文的领域建模.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/104 实践 培训上下文的业务需求.md.html">104 实践 培训上下文的业务需求.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/105 实践 培训上下文的领域分析建模.md.html">105 实践 培训上下文的领域分析建模.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/106 实践 培训上下文的领域设计建模.md.html">106 实践 培训上下文的领域设计建模.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/107 实践 培训上下文的领域实现建模.md.html">107 实践 培训上下文的领域实现建模.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/108 实践 EAS 系统的代码模型.md.html">108 实践 EAS 系统的代码模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/109 后记:如何学习领域驱动设计.md.html">109 后记:如何学习领域驱动设计.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>061 事件风暴与业务全景探索</h1>
<p>我在尝试事件风暴之前,纳闷于 Alberto Brandolini 为何选择“事件”作为领域建模的突破口。倘若不选择 EDAEvent Driven Architecture事件驱动架构或者 CQRS 架构模式,事件是否真正重要呢?</p>
<h3>理解事件的本质</h3>
<p>Martin Fowler 认为:“重要的事件肯定会在系统其他地方引起反应,因此理解为什么会有这些反应同样也很重要。”显然,事件意味着一种<strong>因果关系</strong>,这就使得这样一个静态的概念,其实隐藏着流动的张力。在识别和理解事件时,可以考虑为什么要产生这一事件,以及为什么要响应这一事件,进而思考响应事件的后续动作,从而驱动着设计者的“心流”不断思考下去,犹如搅动了一场激荡湍急的风暴。</p>
<p>不同的团队角色在思考事件时,看到的可能是事物的不同一面,事件犹如棱镜一般将不同色彩的光线折射到每个人的眼睛之中:</p>
<ul>
<li>事件对于业务人员:事件前后的业务动作是什么?产生了什么样的业务流程?</li>
<li>事件对于管理人员:事件导致的重要结果是什么?会否影响到管理和运营?</li>
<li>事件对于技术人员:是什么触发了事件消息?当事件消息发布时,谁来负责订阅和处理事件?</li>
</ul>
<p>虽然关注点不同,但事件却能够让这些不同的团队角色“团结”到一个业务场景下,体会到统一语言的存在。业务场景仿佛是一条新闻报道,团队的参与角色就是阅读新闻报道的读者,他们关注新闻的目的各不相同,却又不约而同地被同一个新闻标题所吸引。这个新闻的标题,就是<strong>事件</strong>。例如 2019 年 6 月 17 日,沪伦通正式启动,国内外新闻媒体皆有报道,如下图所示:</p>
<p><img src="assets/61d498b0-bca1-11e9-a349-65f0a13339ef" alt="42668551.png" /></p>
<p>当这条影响国内甚至国际金融界的重磅事件发布之后吸引了许多人尤其是广大投资人的眼球。角色不同对这一新闻事件的着眼点也不相同。经济学家关心此次事件对证券交易市场特别是对上交所、伦交所带来的影响政治家关心这种金融互通机制对中英以及中欧之间政治格局带来的影响股市投资人关心如何进入沪伦通进行股票交易以谋求高额投资回报证券专业人士则关心沪伦通这种基于存托凭证DR的跨境转换方式和交易模式……不一而足你方唱罢我登场关心的却是同一个新闻事件。</p>
<p>之所以将事件比喻为新闻,还在于它们之间存在本质的共同点:它们都是过去已经发生的事实。新闻不可能报道未来,即使是对未来的预测,预测这个行为也是在过去的某个时间点发生的。整条新闻报道的背景就是该事件的场景要素:</p>
<table>
<thead>
<tr>
<th align="left">场景要素</th>
<th align="left">新闻</th>
<th align="left">事件</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">What</td>
<td align="left">报道的新闻</td>
<td align="left">发布的事件</td>
</tr>
<tr>
<td align="left">When</td>
<td align="left">新闻事件的发生时间</td>
<td align="left">何时发布事件</td>
</tr>
<tr>
<td align="left">Where</td>
<td align="left">新闻事件的发生地点</td>
<td align="left">在哪个限界上下文的哪个聚合</td>
</tr>
<tr>
<td align="left">Why</td>
<td align="left">为何会发生这样一起新闻事件</td>
<td align="left">发布事件的原因以及事件结果的重要性</td>
</tr>
<tr>
<td align="left">Who</td>
<td align="left">新闻事件的牵涉群体</td>
<td align="left">谁发布了事件,谁订阅了事件</td>
</tr>
<tr>
<td align="left">How</td>
<td align="left">新闻事件的发生经过</td>
<td align="left">事件如何沿着时间轴流动</td>
</tr>
</tbody>
</table>
<p>在运用事件风暴时,我们可以像一名记者那样敏感地关注着一些关键事件的发生,并按照时间轴的顺序把这些事件串起来。设想乘坐地铁的场景:</p>
<ul>
<li>车票被购买了TicketPurchased我只关心票买了并不关心是怎么支付的</li>
<li>车票有效TicketAccepted我只关心闸机认可了车票的有效性并不关系是刷卡还是插入卡片</li>
<li>闸机门打开StationGateOpened门打开了是刷卡有效的结果意味着我可以通行我并不关心之前闸机门的状态例如某些地铁站在人流高峰期会保持闸机门常开</li>
<li>乘客通过闸机门PassengerPassed我一旦通过闸机就可以等候地铁准备上车我并不关心通过之后闸机门的状态</li>
<li>地铁到站MetroArrived是否是我要乘坐的地铁到站如果是我就要准备上车我并不关心地铁是如何行驶的</li>
<li>地铁车门打开MetroGateOpened只有车门打开了我才能上车我并不关心车门是如何打开的</li>
<li>……</li>
</ul>
<p>这就是与时间相关的一系列事件。分析乘坐地铁的业务场景,识别出一系列“关键事件”并将其连接起来,就会形成一条显而易见的基于时间轴的事件路径。如下图所示:</p>
<p><img src="assets/1ad7a7b0-bca4-11e9-b095-45b8601f64cd" alt="45043023.png" /></p>
<p>以事件为领域分析建模的关注起点,就可以让开发团队与业务人员(包括领域专家)都能够关注每个环节的结果,而不考虑每个环节的实现。事件可以让整个团队在事件风暴过程中统一到领域模型中。同时,这种以“事件”为核心的建模思路,实则也改变了我们观察业务领域的世界观。在事件风暴的眼中,领域的世界是一系列事件的留存。这些因业务活动留下的不可磨灭的足迹牵涉到状态之迁移,事实之发生,它们忠实地记录了每次执行命令后产生的结果。如上所述,乘坐地铁的事件路径实则是乘客、闸机、地铁等多个领域对象的状态迁移。这种状态迁移过程体现了业务之间的<strong>因果关系</strong></p>
<p>事件风暴的事件通常被称之为“领域事件”,它具备以下四个特征:</p>
<ul>
<li>领域事件是过去发生的与业务有关的事实</li>
<li>领域事件是管理者和运营者重点关心的内容,若缺少该事件,会对管理与运营产生影响</li>
<li>领域事件具有时间点的特征,所有事件连接起来会形成明显的时间轴</li>
<li>领域事件会导致目标对象状态的变化</li>
</ul>
<p>既然事件代表一个已经发生的事实,因此就应该使用动词的过去时态来表达,例如 OrderConfirmed 事件、OrderCompleted 事件。从自然语言的语法角度讲中文确乎不适合描述事件因为中文语法并没有“时态”的概念这使得我们在描述事件时显得词语过于贫乏只能加上“已”字来体现它是过去发生的。OrderCreated 事件被描述为“订单已创建”OrderCompleted 事件被描述为“订单已完成”。因为没有时态,使得我们对时间不那么敏感,在进行事件建模时,稍显不适应,这是我在很多团队中感受到的现象。</p>
<p>我在最初接触事件风暴时,正是考虑到多数人对事件的不够敏感,又或者建模世界观的不易扭转,倾向于围绕着“用例”进行建模。多数开发人员更容易理解用例而非事件,领域专家也能接受用例的形式;但它与事件相比,也存在一些天生的不足:</p>
<ul>
<li>用例没有时间的概念,它是对业务场景和业务功能的静态划分,无法形成动态的流程,往往要用活动图或流程图来弥补;</li>
<li>命令和查询是两种不同的操作,它们导致的结果与意义并不相同,而用例却未曾区分它们。</li>
</ul>
<p>事件弥补了用例的不足。事件自身具备时间特征,使得业务场景的事件一经识别,就能形成动态的流程。由于事件会导致目标对象状态的变更,说明唯有命令才会触发事件,这就要求我们在开展事件风暴时,需要区分命令和查询。除此之外,事件在参与业务流程中,代表了不同时刻的因果关系。首先,事件是“果”。触发事件的起因包括:</p>
<ul>
<li>由用户活动触发:例如用户将商品加入到购物车,触发 ProductAddedToCart 事件。</li>
<li>当条件满足时:提交订单后超过规定时间未支付,触发 OrderCancelled 事件。</li>
<li>外部系统:支付系统返回交易凭证,触发 PaymentCompleted 事件。</li>
</ul>
<p>事件又是“因”。当事件发布之后所有关心该事件的订阅者随之会执行新的命令触发下一个流程步骤。例如支付完成PaymentCompleted事件会触发准备订单Prepare Order的命令。</p>
<p>事件的因与果体现为事件的发布与订阅,这两种对事件的操作形成了因果关系的不断传递。</p>
<h3>事件风暴建模工作坊</h3>
<p>事件风暴是一种高度强调交流与协作的可视化工作坊,是大白纸与各色即时贴的重度使用者。面对着糊满整面墙的大白纸,工作坊的参与人员通过充分地交流与沟通,然后用马克笔在各色即时贴上写下各个领域模型概念,贴在墙上呈现生动的模型。由于这些模型都是可视化的,就可以给团队直观印象。大家站在墙面前,观察这些模型,及时开展讨论。若发现有误,就可以通过移动即时贴来调整与更新,也可以随时贴上新的即时贴完善建模结果。</p>
<p>Alberto Brandolini 设计的事件风暴通常分为两个层次。如果在工作坊过程中将主要的精力用于寻找业务流程中产生的领域事件,则这个过程可以认为是宏观级别的事件风暴,其目的是<strong>探索业务全景Big Picture Exploration</strong>。在识别出全景事件流之后就可以标记时间轴的关键时间点作为划分领域边界和限界上下文边界的依据同时也可以基于事件表达的业务概念对领域进行划分最终确定候选的子领域和限界上下文。另一个层次则属于设计级别Design-level的领域分析建模方法通过探索业务全景获得的事件流<strong>围绕着事件获得领域分析模型</strong>。这些领域分析建模要素除了领域事件之外,还包括决策命令、读模型和聚合。事件风暴的领域分析建模方法通常会以业务全景探索的结果作为领域分析建模的基础。</p>
<h4>探索业务全景</h4>
<p>在探索业务全景的过程中,为了使每个人保持专注,一开始要排除其余领域概念的干扰,一心寻找沿着时间轴发展的事件。事件是事件风暴的主要驱动力,寻找出来的事件则是领域分析模型的骨架。事件风暴使用<strong>橙色即时贴</strong>来代表一个<strong>事件Event</strong></p>
<p>事件风暴工作坊要求沿着时间轴对事件进行识别。通常的做法是由领域专家贴上第一张他/她最为关心的事件,然后由大家分头围绕该事件写出在它之前和之后发生的事件,并按照时间顺序由左向右排列。以信用卡申请开卡的业务为例,领域专家认为“开卡申请已审批”是我们关注的核心事件,于是就可以在整面墙的中间贴上橙色即时贴,上面写上“开卡申请已审批”事件:</p>
<p><img src="assets/c33be630-bca1-11e9-ac77-f5b1a77a87b3" alt="40805614.png" /></p>
<p>在确定这个核心事件之后,我们就要以此为中心,向前推导它的起因,向后推导它的结果,根据这种因果关系层层推进,逐渐形成一条或多条沿着时间轴且彼此之间存在因果关系的事件流:</p>
<p><img src="assets/d23424e0-bca1-11e9-8296-ad04873de5ea" alt="41108561.png" /></p>
<p>在识别事件的过程中,工作坊的参与人员应尽可能<strong>站在管理和运营的角度去思考领域事件</strong>。这里所谓的“因果关系”,也可以理解为产生事件的前置条件是什么,由此推导出前置事件;事件导致的后置条件是什么,由此推导出后置事件。</p>
<p>从“开卡申请已审批”事件往前推导,它的前置条件是什么呢?显然,只有在信用卡申请人提交了开卡申请之后才可能审批申请,由此得到前置事件“开卡申请已提交”。以此类推,“开卡申请已提交”的前置条件又是什么呢?申请人在提交申请信息之前,需要通过征信系统对填写的内容做征信预检,于是可推导出前置事件“征信预检已完成”:</p>
<p><img src="assets/ddac8ba0-bca1-11e9-ac77-f5b1a77a87b3" alt="41397246.png" /></p>
<p>从“开卡申请已审批”事件往后推导,它的后置条件是什么呢?如果开卡申请通过了,一方面保证申请人收到审批结果通知,另一方面则开始制卡,首先就需要保证信用卡号已经生成,由此得到两个并行的后置事件“卡号已生成”和“审批结果已通知”。接着,在“卡号已生成”事件之后,就是等待制作信用卡的结果,由此获得后置事件“信用卡制作完毕”:</p>
<p><img src="assets/e7ed83d0-bca1-11e9-a349-65f0a13339ef" alt="41821315.png" /></p>
<p><strong>事件风暴是一种探索性的建模活动</strong>。在探索事件的过程中,我们不要急于去识别其他的领域对象,基于事件结果,也不要急于去寻找导致事件发生的起因。尤其是在探索业务全景期间,更要如此。毕竟人的注意力是有限的。从一开始,就应该让工作坊的参与人员集中精力专注于事件。倘若存在疑问,又或者需要提醒业务人员或技术人员特别注意,可以用<strong>粉红色即时贴</strong>来表达该警告信息Alberto Brandolini 将其称之为“热点 Hot Spot”。例如针对“开卡申请已审批”事件需要考虑审批未通过的异常情况“卡号已生成”事件需要考虑不同类型的信用卡需遵循不同的卡号生成规则“审批结果已通知”事件可以标记系统支持的通知方式</p>
<p><img src="assets/f40c8ad0-bca1-11e9-ac77-f5b1a77a87b3" alt="42699590.png" /></p>
<p>如前所述,触发事件的起因包括三种可能。在事件风暴业务全景探索过程中,可以在获得全景事件流之后,判断各个事件的起因,并分别用不同颜色的即时贴进行标记:</p>
<ul>
<li>由用户活动触发:标记参与事件的用户角色,用<strong>黄色小即时贴</strong>绘制火柴棍人表示</li>
<li>当条件满足时:标记引起事件的策略,用<strong>紫色即时贴</strong>表示</li>
<li>外部系统:标记引起事件的外部系统,用<strong>浅粉色即时贴</strong>表示</li>
</ul>
<p>前面获得的事件流可以表示为:</p>
<p><img src="assets/fef9c890-bca1-11e9-b095-45b8601f64cd" alt="43430478.png" /></p>
<p>不要小看对这些事件起因的标记。在完成全景事件流之后,对事件的起因进行再一次梳理有助于团队就识别的事件达成一致,检查事件是否存在疏漏、谬误之处。作为事件起因的用户、外部系统与策略还为后面的领域分析建模奠定基础。其中,识别出的外部系统也有助于未来的架构设计,帮助我们绘制《领域驱动战略设计实践》课程中讲到的 C4 模型中的系统上下文System Context图。</p>
<h3>分享交流</h3>
<p>我们为本课程付费读者创建了微信交流群,以方便更有针对性地讨论课程相关问题。入群方式请到第 6-1 课末尾添加小编的微信号并注明「DDD」。</p>
<p>阅读文章过程中有任何疑问随时可以跟其他小伙伴讨论,或者直接向作者提问(作者看到后抽空回复)。<strong>你的分享不仅帮助他人,更会提升自己。</strong></p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/领域驱动设计实践(完)/060 案例 订单核心流程的四色建模.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/领域驱动设计实践(完)/062 事件风暴与领域分析建模.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997e847e9a3cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>