learn.lianglianglee.com/专栏/领域驱动设计实践(完)/062 事件风暴与领域分析建模.md.html
2022-05-11 19:04:14 +08:00

875 lines
44 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>062 事件风暴与领域分析建模.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 href="/专栏/领域驱动设计实践(完)/061 事件风暴与业务全景探索.md.html">061 事件风暴与业务全景探索.md.html</a>
</li>
<li>
<a class="current-tab" 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>062 事件风暴与领域分析建模</h1>
<p>在确定了全景事件流之后,可以在战略设计层面继续精进,鉴别出领域与限界上下文的边界。这里略过不提,且进入战术设计阶段的领域分析建模。</p>
<h3>事件风暴的分析模型要素</h3>
<p>通过事件风暴进行领域分析建模,其核心的模型要素就是“事件”。除此之外,参与事件风暴的分析模型要素还包括决策命令、读模型、策略和聚合。其中,事件和策略已经在探索业务全景的时候进行了初步识别。</p>
<h4>决策命令</h4>
<p>通观事件之起因,除了外部系统是直接发布事件之外,无论是用户活动,还是满足某个条件,都需要一个<strong>命令Command<strong>来响应它才是直接导致事件发生的“因”。在事件风暴中Alberto Brandolini 将命令称之为“决策命令Decision Command使用</strong>浅蓝色即时贴</strong>表示。决策命令往往由动宾短语组成,例如 Place Order、Send Invitation 等。</p>
<p>由于决策命令和事件存在因果关系因此二者往往是一一对应的。例如Cancel Order 决策命令会触发 OrderCancelled 事件Subscribe Course 决策命令会触发 CourseSubscribed 事件。正是这种一一对应关系,使得它们存在语义上的重叠,区别仅在于时态。故而有的事件风暴实践者认为可以在事件风暴中省略决策命令。我并不敢苟同这一观点,相反,我反而极为强调<strong>决策命令在事件风暴中的重要性</strong>,它是领域分析建模的一个重要驱动力,因为通过<strong>它连接了用户、策略、聚合、读模型和事件</strong>,如下图所示:</p>
<p><img src="assets/d54afa70-bca4-11e9-b095-45b8601f64cd" alt="74748976.png" /></p>
<p>从图中可以看出,由<strong>事件</strong>可以驱动出<strong>决策命令</strong>,在它们之间借由<strong>聚合</strong>对象来发布事件。当事件发生后,如果某个<strong>策略</strong>满足条件,也会引发决策命令,而<strong>用户</strong>在引发决策命令时,需要足够的<strong>读模型</strong>来帮助它做出正确的决策。</p>
<p>那么该如何正确地理解决策命令显然Alberto Brandolini 使用决策来修饰命令并非空穴来风因为这一名词突出了命令往往需要更多的信息来帮助参与者Actor做出决策。参与者是用例图的设计要素在事件风暴中可以认为是对所有事件起因的抽象用户、条件满足如定时器与外部系统。其中外部系统对我们而言是一个黑盒子不用考虑它是如何触发了事件因而可以忽略。因此参与者在基于业务场景做出决策时需要如下两方面数据的支撑</p>
<ul>
<li>信息:必须基于足够充分的信息才能做出正确的决策,提供这些信息的对象被称之为<strong>读模型Read Model</strong>,在事件风暴中用<strong>浅绿色即时贴</strong>表示。</li>
<li>策略:根据业务规则,当某个条件满足时,会触发一个决策命令,这个业务规则被命名为<strong>策略Policy</strong>,在事件风暴中用<strong>紫色标签</strong>表示。</li>
</ul>
<h4>读模型和策略</h4>
<p>当决策命令由用户引发时可以确认该决策命令的发生是否需要提供足够的读模型信息。读模型是用户通过查询操作获得的。若不具备这一信息可能不足以支持用户执行决策命令。例如买家希望提交订单就需要先查看购物车获得购物车内容然后才能执行下订单Place Order的决策命令触发 OrderCreated 事件。这时,查看购物车获得的结果 ShoppingCart 就是读模型:</p>
<p><img src="assets/eb379e60-bca4-11e9-8296-ad04873de5ea" alt="img" /></p>
<p>读模型是用户执行决策命令必需的输入信息在代码层面这些读模型就是执行决策命令的领域行为所需的输入参数。用户发起决策命令的方式是因为执行了某个活动例如决策命令“提交订单”实则是因为用户点击了“提交订单”按钮。用户活动的执行与用户体验User eXerperienceUX直接有关。现实世界的业务场景通过用户体验将用户与读模型结合起来把信息传输给事件风暴的决策命令。这一过程牵涉到用户、查询和命令操作恰好符合组成用例的要素。若建模人员熟悉用例也可借助用例图来分析。</p>
<p>注意,上图是将读模型 ShoppingCart 提供给 Place Order 决策命令,而非查询操作与命令操作之间的交互。有的事件风暴实践者将查询操作也纳入到事件风暴的模型中,认为是用户执行查询操作获得读模型后,触发了决策命令,如下图所示:</p>
<p><img src="assets/f96deca0-bca4-11e9-ac77-f5b1a77a87b3" alt="58315254.png" /></p>
<p>我认为这样的模型设计并不恰当,因为它<strong>将活动流程图与事件的因果关系混为一谈</strong>了。实际上,活动流程图反应了现实世界的问题域,事件风暴表现的事件因果关系却是解决方案域的内容,这是领域建模活动中两个不同的层次。买家先查询购物车,然后提交订单,这是买家的操作流程。但从事件的因果关系看,并非“查询购物车”触发了“提交订单”这个决策命令,而是用户通过查询获得了购物车读模型之后,由用户发起“提交订单”的决策命令,再通过订单聚合发布了 OrderCreated 事件。“查询购物车”和“提交订单”是两个不同的用户活动,它们并不具有时序上的连续性,可以认为是两个独立的业务场景。由于查询操作并不会触发事件的发生,从模型上看,它也不会导致命令的发生,因而在事件风暴中,<strong>并没有查询操作的位置</strong>,而是以读模型的形式出现。这也变相地促使建模人员在识别用户活动时,需要分辨该活动究竟是查询还是命令,有利于 CQRS 模式的落地。</p>
<p>当决策命令由策略引发时,就表示事件发生后某些数据满足了某条业务规则。一旦该策略被满足,就会引起目标对象的状态变更,然后根据业务规则的规定触发下一个决策命令。例如,策略“提交订单后,一旦超过规定时间未支付,则取消订单”会触发 Cancel Order 命令,从而引起 OrderCancelled 事件的发生。策略引发的决策可以是自动的如定时器检测到支付时间超时也可以是用户手动触发如用户登录时输入错误密码的次数太多还可以二者并存如在取消订单业务场景中Cancel Order 命令既可以由定时器自动触发,也可以由用户手动触发。</p>
<h4>聚合</h4>
<p>虽然决策命令和事件之间存在因果关系但事件并非直接由决策命令发布而是借助一个“媒介”来发布事件。这个媒介就是“聚合Aggregate”。聚合在事件风暴中使用<strong>黄色大即时贴</strong>来表示。<strong>聚合划分了现实世界和模型世界之间的界线</strong>。在现实世界,是用户执行了决策命令触发了事件;在模型世界,是聚合履行了发布事件的职责。例如,在电商系统的业务流程中,现实世界的用户活动是用户提交了订单;在模型世界,是 Order 聚合发布了 OrderCreated 事件。</p>
<p>寻找聚合的过程可能是一个艰难的过程。由于聚合是构成领域分析模型的核心要素,识别聚合需要审慎,不要轻易下结论。若未寻找到它,可以先贴上一个空白的黄色大即时贴表示这里存在一个聚合,但目前还不知道它的名字。</p>
<p>在事件风暴中,我们也可以利用事件来反向寻找聚合。分析事件的特征,由于它是由决策命令触发的,意味着事件的产生会带来<strong>目标对象状态的变化</strong>。状态的变化分为三种形式:</p>
<ul>
<li>从无到有:意味着创建,例如“订单已创建”事件标志着新订单的产生。</li>
<li>修改属性值:意味着值的更新,例如“订单已取消”事件使得订单从之前的状态变更为“已取消”状态;也可能意味着内容的变化,例如“商品被加入到购物车”事件,说明购物车增加了一个新的条目。</li>
<li>从有到无:意味着删除,不过在多数项目中并不存在这种状态变化;表面是删除,实际是修改属性值。例如“会员已注销”事件和“商品已下架”事件,实则都不是直接删除会员和商品记录,而是将该记录的状态置为“已注销/已下架”状态。</li>
</ul>
<p>显然,发生状态变更的对象有很大几率就是我们要寻找的聚合对象。毕竟聚合对象承担了发布事件的职责,而事件又是由于状态变更而产生。谁能准确地侦知状态是否变更以及何时发生变更?我想,只有拥有状态的聚合对象自身才具备这一能力。</p>
<h3>事件风暴的领域分析建模过程</h3>
<p>显然,围绕着“事件”为中心,事件风暴给出了一条有章可循的领域分析建模路径。领域分析建模的基础是探索业务全景的产出物,即业已识别出来的事件流,以及参与事件流的用户、策略与外部系统。整个领域分析建模的过程如下:</p>
<ul>
<li>第一步:挑选任意一个与用户有关的事件,反向驱动出决策命令,该用户就是发出决策命令的人(角色)。从事件驱动出决策命令非常容易,就是将事件的过去时态转换为动宾形式的决策命令即可。</li>
<li>第二步:根据决策命令与事件之间的因果关系,推导出要发布该事件必须的前置信息,即决策所需的读模型。读模型通常由用户通过查询操作获得,可以理解为是决策命令行为的输入参数。</li>
<li>第三步:根据事件状态变更的目标,决定决策命令与事件之间的聚合对象。若无法确定,则保留一个空的黄色即时贴,待以后确定。</li>
<li>第四步:选择当前事件的后置事件。若后置事件仍然与用户有关,则重复第一步;若后置事件与外部系统有关,可以跳过该事件的建模,继续选择下一个后置事件。若事件与策略有关,在进一步细化策略对象之后,驱动出决策命令,重复第三步。</li>
</ul>
<p>以前面所示的信用卡开卡事件流为例,我们依次选择以下三个事件:</p>
<p><img src="assets/172e8060-bca5-11e9-8296-ad04873de5ea" alt="img" /></p>
<p>首先是审批人参与的“开卡申请已审批”事件,执行第一步,由该事件可以反向驱动出决策命令“审批开卡申请”。第二步是根据决策命令推导出触发事件需要的读模型。审批开卡申请的前置信息是“申请”和“用户征信”,若缺乏这两个信息,审批人无法做出“审批开卡申请”的决策。第三步是确定决策命令与事件之间的聚合对象。显然,“开卡申请已审批”事件影响到的就是申请的状态,它就是我们要寻找的聚合对象:</p>
<p><img src="assets/20e512e0-bca5-11e9-ac77-f5b1a77a87b3" alt="img" /></p>
<p>接着进入第四步,选择下一个后置事件“卡号已生成”。该事件与策略有关,细化策略为“卡号规则”。由事件驱动出决策命令为“生成卡号”,进入第三步,识别两者之间的聚合对象。卡号的生成影响了信用卡的属性,可以认为该事件影响状态的目标对象为“信用卡”:</p>
<p><img src="assets/2a10a370-bca5-11e9-ac77-f5b1a77a87b3" alt="64620751.png" /></p>
<p>继续第四步,选择下一个后置事件“信用卡制作完毕”。由于该事件由外部系统发布,可以忽略该建模过程,仅仅标记外部系统即可:</p>
<p><img src="assets/330397d0-bca5-11e9-8296-ad04873de5ea" alt="64725757.png" /></p>
<p>通过这个简单案例,可以清晰地看到我总结的领域分析建模过程具有一定的可操作性。事件风暴工作坊的参与人员可以按照建模步骤一步一步执行。执行每一步都需要团队与领域专家进一步讨论和确认,保证识别出来的模型对象遵循该领域的统一语言。在这个分析建模过程中,每个模型对象都有着建模的参考依据,包括模型对象的身份特征、彼此之间的关系、承担的职责,这就在一定程度上减轻了对建模人员经验的依赖。</p>
<p>事件风暴的两个层次恰好可以对应领域驱动设计的战略阶段与战术阶段。前者主要用于识别限界上下文,后者主要用于建立领域分析模型,这恰恰填补了 Eric Evans《领域驱动设计》书中的关键空白。当然Alberto Brandolini 提出的事件风暴不仅于此,它还能用于企业的流程改进、业务创新和对新型服务的探索。这些实践与领域驱动设计没有直接关系,这里就不再叙述。若有兴趣了解事件风暴的更多内容,可以访问<a href="https://www.eventstorming.com/">事件风暴的官方网站</a></p>
<h3>分享交流</h3>
<p>我们为本课程付费读者创建了微信交流群,以方便更有针对性地讨论课程相关问题。入群方式请到第 6-1 课末尾添加小编的微信号并注明「DDD」。</p>
<p>阅读文章过程中有任何疑问随时可以跟其他小伙伴讨论,或者直接向作者提问(作者看到后抽空回复)。<strong>你的分享不仅帮助他人,更会提升自己。</strong></p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/领域驱动设计实践(完)/061 事件风暴与业务全景探索.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/领域驱动设计实践(完)/063 案例 订单核心流程的事件风暴.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":"70997e86dc1f3cfa","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>