learn.lianglianglee.com/专栏/领域驱动设计实践(完)/105 实践 培训上下文的领域分析建模.md.html
2022-05-11 19:04:14 +08:00

1033 lines
42 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>105 实践 培训上下文的领域分析建模.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 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 class="current-tab" 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>105 实践 培训上下文的领域分析建模</h1>
<p>培训上下文具有一定的独立性,从创建培训计划到分配票、提名与培训形成了非常清晰的业务流程,取消票的流程作为核心领域逻辑也需要进行深入细致地分析建模,因此我采用了事件风暴对其进行领域分析建模。</p>
<h3>识别事件</h3>
<p>事件风暴的关键在于识别事件。遵循一条隐含的时间轴我们寻找领域专家最为关心的一个关键事件那就是“员工被提名Employee Nominated”事件。遵循统一语言的要求被提名参加培训的员工被称之为“候选人Candidate因此该事件更准确的描述应为“候选人被提名Candidate Nominated”。现在在墙面上贴下第一个关键领域事件并以橙色即时贴表示</p>
<p><img src="assets/57e1c3f0-3618-11ea-9174-f9b762424577" alt="38840209.png" /></p>
<p>有了第一个核心领域事件,我们就可以分别按照向前向后的事件驱动力顺序识别领域事件。在识别领域事件时,要注意结合业务流程,遵循统一语言的定义,并根据领域事件的特征确定识别出来的是否领域事件。</p>
<h4><strong>由右向左的逆向推动</strong></h4>
<p>这个方向其实就是逆向地从果推向因。分析培训的业务流程,一名员工若要获得提名,其前提条件是部门协调者获得票,对应的领域事件为 TicketAssignedToCoordinator。根据处理票的统一语言培训专员或部门协调者分配票给协调者称之为“分配Assignment协调者分配票给参加培训的员工称之为“提名Nomination”。这里属于分配的语义该领域事件可以简化为 TicketAssigned。</p>
<p>从流程看培训专员在分配票给协调者之前需要设置过滤器。设置过滤器的目的是为了快速高效地获得候选人名单本质上并非分配票操作的前置条件可以认为“过滤器已配置FilterConfigured”是一个单独的事件。这也正好体现了业务流程与事件风暴的不同之处。协调人要获得票首先需要有培训票。这个所谓的“票”其实就是培训名额因此票被分配的前提是培训已经被创建由此可以获得前置事件“培训已创建TrainingCreated”。该事件其实是培训上下文启动培训流程的起点。由此可以依次获得如下领域事件</p>
<p><img src="assets/696170d0-3618-11ea-b651-9bf55e9590d3" alt="33534135.png" /></p>
<h4><strong>由左向右的正向推动</strong></h4>
<p>正向推动的思考方向是从起因推结果,即分析当前领域事件发生后,会产生什么样的结果?这一思考方向相较于逆向推动更为容易。不过,正向逆向两个方向的驱动力实则可互为补充,让识别领域事件的过程更加严谨而周密,因而不可偏废。从 CandidateNominated 事件开始整个过程围绕着“票Ticket”推进、迁移和变化票状态的迁移恰好与触发领域事件相对应算是识别领域事件的一个助力。</p>
<p>对候选人提名之后系统会等待候选人的确认这会带来三个不同的分支由此产生三个不同的领域事件票已注册TicketEnrolled、票已拒绝TicketDeclined与截止日期已匹配DeadlineMatched。其中被拒绝的票会被培训专员重新分配这相当于重复进入提名的流程。既然事件流已经表达了提名过程中触发的领域事件到该领域事件之后就无需重复重新提名的过程。若在探索业务全景阶段当我们按照时间轴规范了领域事件的顺序之后可以用箭头来表示关键事件发布后的后续流程例如将TicketDeclined指向 CandidateNominated 事件。<strong>领域分析建模阶段的目的是通过事件风暴寻找领域概念</strong>,流程只是辅助我们判断识别出来的领域事件是否存在疏漏,仅此而已。</p>
<p>在提名候选人之后,培训票相当于已被占用。取决于不同的状态,不同的角色都可以在适当时间取消票,产生 TicketCancelled 事件。票的取消固然发生在提名之后但它的流程却是相对独立的因此可以为其单独建立一个事件流并用热点HotSpot标记该事件发生在 CandidateNominated 事件之后。如果是候选人自己取消票,由于业务规则不允许候选人直接取消票,需要通过审批,故而可以认为是一次取消申请,产生的事件为 CancellationApplied。</p>
<p>如果票最终确认并满足培训开始时间即进入培训阶段的培训管理流程。这个流程从培训已开始TrainingStarted事件起从培训已结束TrainingEnded期间牵涉到对培训、学员以及票的相关领域事件。参加培训的每位学员都要进行考勤培训完毕后会关闭培训票对应的领域事件依次为 TraineeAttended 和 TicketClosed。对于学员与票而言还牵涉到一个分支流程就是学员未能出席此次培训需要记录为缺勤并加入到黑名单票作废对应的领域事件为 TraineeNotShown、TraineeAddedToBlacklist 和 TicketLost。</p>
<p>结合业务流程与票的状态图,从 CandidateNominated 事件开始,可以获得如下事件流:</p>
<p><img src="assets/83ffdd00-3618-11ea-a700-29da27227d28" alt="53654140.png" /></p>
<p>在更改票状态的领域事件上标记了一个热点要求保存每次票变更的历史记录。如果不标记该热点就会丢失这一重要的需求信息同时又不必为票每次发生票状态变更的事件都添加“票历史记录已创建TicketHistoryCreated”领域事件。</p>
<h3>识别参与者</h3>
<p>在识别事件之后,我们应该按照时间轴的顺序根据业务流程梳理这些事件,以判断是否存在缺失事件或错误事件。为每个事件识别参与者,既可以明确是谁触发了事件,进一步确定事件与事件之间的因果关系,又可以结合参与者与场景完成对事件的梳理。</p>
<p>领域事件一共有四种参与者Actor</p>
<ul>
<li>角色Role触发事件的人</li>
<li>策略Policy触发事件的规则通常是随着时间的推移满足规则要求的时间条件后会自动触发事件</li>
<li>外部系统External System由当前系统外的其他系统触发事件</li>
<li>事件Event由当前事件的前置事件直接触发在事件风暴中无需表示</li>
</ul>
<p>培训上下文事件流识别出来的参与者及其对应领域事件如下所示:</p>
<p><img src="assets/9033cb90-3618-11ea-a962-5985f456c479" alt="33776222.png" /></p>
<p>DeadlineMatched 领域事件会在培训专员设置的截止日期到达时触发,故而它的参与者是一个策略。这个策略对应的时间规则是截止日期,它是由培训专员配置的,我们应增加一个 ValidDateConfigured 领域事件。培训专员或协调者在取消票时培训票可能会根据事先设定的活动Action对票进行处理若活动为 LostAction票就会作废产生 TicketLost 领域事件。对活动的配置也是之前识别事件时未考虑到的,需要增加一个 TicketActionConfigured 领域事件。</p>
<p>整个培训上下文没有与外部系统发生任何协作,故而没有外部系统参与者参与到整个事件流。</p>
<h3>领域分析建模</h3>
<p>围绕着领域事件分别驱动出决策命令、写模型Alberto Brandolini将其称为聚合为了避免与领域设计模型中的聚合混淆且领域事件通常会改变目标对象的状态故而称为写模型与读模型相对和读模型。写模型与读模型共同组成了领域分析模型。</p>
<p>从 TrainingCreated 领域事件到 TicketConfirm 领域事件进行分析建模的结果如下所示:</p>
<p><img src="assets/a43abf90-3618-11ea-a0e3-63013d6e72ad" alt="34086141.png" /></p>
<p>参与者、决策命令与领域事件共同组成一个领域场景参与者在执行决策命令时需要提供必要的读模型Read Model才能完成对写模型Write Model状态的修改从而发布领域事件。以 TrainingCreated 领域事件为例培训专员PO要创建一个培训需要提供培训起止日期、培训状态、课程与教师的信息才能创建一个信息完整的培训对象。培训对象是从无到有创建出来是改变了状态的写模型其余对象为创建培训决策命令需要的读模型。</p>
<p>在对 TicketActionConfigured 领域事件进行领域分析建模时,事件对应了两个不同的决策命令:</p>
<ul>
<li>配置有效日期时设置的 TicketAction</li>
<li>分配票时设置取消时的 TicketAction</li>
</ul>
<p>这两个决策命令产生的领域事件看似相同,实则会产生不同的活动,这意味着需要发布不同的领域事件:</p>
<p><img src="assets/e57bc3f0-3618-11ea-bb50-5d7e0e1eba80" alt="33918189.png" /></p>
<p>与取消票相关的领域事件操作的皆为Ticket写模型对它们进行分析建模的结果如下所示</p>
<p><img src="assets/eec033b0-3618-11ea-b651-9bf55e9590d3" alt="75658055.png" /></p>
<p>对从培训开始到结束的领域事件进行分析建模的结果如下所示:</p>
<p><img src="assets/f74487c0-3618-11ea-996b-ef6591d33435" alt="53756107.png" /></p>
<p>领域分析建模阶段的关键是识别领域概念,为限界上下文的领域建立抽象模型。培训上下文的领域事件处于同一个限界上下文,因而只需要考虑该上下文内部领域模型之间的关系。由此可以获得如下领域分析模型:</p>
<p><img src="assets/005f2860-3619-11ea-a962-5985f456c479" alt="35059882.png" /></p>
<p>事件风暴识别出来的写模型与读模型共同组成了领域分析模型。在上图所示的领域分析模型中,还增加了一个之前未识别出来的 TicketHistory 领域类,它是通过标记的热点识别出来的,用以记录每次票状态的变更历史。</p>
<p>领域分析模型并非一成不变,它仅仅代表当前阶段分析建模的产出。随着需求的变化,该分析模型还会随之调整,在进入到领域设计建模与领域实现建模阶段后,也需要随时保证领域分析模型与领域设计模型、领域实现模型的同步。</p>
<p>当前的领域分析模型是一个典型的对象图,领域模型对象之间的关系错综复杂,它们在不同的领域场景中扮演了不同的角色,履行着各自的职责,这些信息在领域分析模型中都无法清晰地呈现出来。因此,需要给这一模型添加设计约束,明确每个领域模型的角色构造型,并根据领域场景确定它们之间的协作顺序,为编码实现提供更为清晰地指导。</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/领域驱动设计实践(完)/104 实践 培训上下文的业务需求.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/领域驱动设计实践(完)/106 实践 培训上下文的领域设计建模.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":"70997ef64ca93cfa","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>