learn.lianglianglee.com/专栏/领域驱动设计实践(完)/032 实践 先启阶段的领域场景分析(下).md.html
2022-05-11 19:04:14 +08:00

1063 lines
46 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>032 实践 先启阶段的领域场景分析(下).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 class="current-tab" 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 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>032 实践 先启阶段的领域场景分析(下)</h1>
<h3>如何有效地识别参与者的用例</h3>
<p>前述内容通过用例形式将所有的主故事都转换成了与参与者有关的用例,那么,在识别用例时,是否有什么经验可循呢?</p>
<h4>用例关系的确定</h4>
<p>一个用例图,往往体现了参与者与用例之间的使用关系,用例与用例之间的包含或扩展关系,有时候还存在用例之间的泛化关系,确定用例之间的关系很重要。<strong>在识别用例时,思考参与者与用例之间的关系会成为一个不错的设计起点。<strong>尤其在先启阶段,我们识别的用例体现了 Cockburn 提出的用例层次中的</strong>用户目标</strong>这恰好对应用例与参与者的“使用Use”关系。从领域场景分析的角度看这个使用关系代表了业务价值。在确定了参与者后你就可以结合主故事与领域场景询问自己“在这个领域场景下该参与者的用户目标是什么”由此可以帮助我们确定该用例是否主用例。</p>
<p>正如对项目用例图中的分析如果考虑编码实现的本质则问题所属迭代、承担人以及问题状态都是问题Issue的属性然而在用例图中我却以“指定问题所属的迭代”、“分配问题给项目成员”、“更新问题状态”此三个主用例与“编辑问题信息”平级因为它们在项目管理中都具有不可替代的业务价值。</p>
<p>与之相反,包含用例与扩展用例是为具有业务价值的主用例提供支持和服务的,识别它们既可以丰富和完善业务逻辑,又可以在后续的用例边界找到属于通用子领域或支撑子领域的业务内容。<strong>这些不直接提供业务价值的用例恰好可能组成单独的限界上下文</strong>。例如,在前面给出的诸多用例图中,诸如“上传附件”、“通知评估人”等用例主要以扩展用例的形式呈现,这些扩展用例体现了各自内聚的关注点,即文件共享与消息通知。</p>
<p>包含用例与扩展用例之间的区别在于两个用例之间的“粘性”。<strong>包含用例为主用例不可缺少之业务环节</strong>,如“指定项目经理”包含用例之于“立项”主用例,如果缺少了指定项目经理操作,立项就是不完整的。<strong>扩展用例为主用例功能之补充</strong>,如“通知立项”扩展用例之于“立项”主用例,即使没有通知立项的相关干系人,也不妨碍立项工作的完成。作为包含用例或扩展用例本身,又可以有属于自己的包含用例或扩展用例,例如“通知项目经理”对“指定项目经理”的扩展:</p>
<p><img src="assets/c99c7840-cae0-11e8-852c-83bf566b6761" alt="enter image description here" /></p>
<p>从功能相关性看,“立项”与“指定项目经理”用例是强相关的,“通知立项”与“立项”用例是弱相关的。因此,<strong>对包含和扩展用例的识别往往会影响到后续对限界上下文的识别。</strong></p>
<p>在识别用例图时,还要注意避免错误的用例关系识别。例如,在项目管理用例图中,团队最初为项目成员参与者识别出“接收问题分配”用例。结合业务场景对此进行检验:当项目经理将问题分配给项目成员后,在业务上确乎存在接受问题的行为;但该行为其实是一个线下行为,属于项目成员之间的一个口头表达;当问题分配给项目成员之后,就已经意味着该问题已经被项目成员接受。因此,这个用例是不合理的。</p>
<h4>用例名应字斟句酌</h4>
<p>在领域场景分析过程中如果我们只满足于用例图的获得无异于买椟还珠。用例图仅仅是我们获得的分析结果但更重要的是我们获得用例图的过程这其中的关键在于团队与领域专家的交流与合作。作为UMLUnified Modeling Language统一建模语言组成部分的用例图已经得到行业的认可。无论是没有技术背景的领域专家还是没有业务背景的技术专家都能很好理解用例图这种可视化的建模语言。</p>
<p>用例名是领域知识的呈现,更是统一语言的有效输入。用例名应采用动宾短语,描述时须字斟句酌,把握每一个动词和名词的精确,动词是领域行为的体现,名词是领域概念的象征,进而这些行为与概念就能再借助领域模型传递给设计模型,最终通过可读性好的代码来体现。当然,在给出中文用例的同时,还应提倡以英文来表述,毕竟在最终的代码层面,还是用英文来“说话”。</p>
<p>在项目管理用例图中我们最初给出的用例为“查看问题完成情况”但在项目管理领域所谓“问题完成情况”仅仅体现了问题的状态却没有清晰地表达问题在迭代周期内的过程。准确的术语是“进度Progress命名为“跟踪问题进度Tracking Issue Progress”更加符合该领域的统一语言。在最初识别用例时对于“创建问题”的包含用例而言最初命名为“问题检查”。这个描述未遵循动宾短语的形式而“检查”一词也容易带来歧义会错以为是项目成员检查问题的完成情况实则是对创建的问题进行合规性验证更名为“验证问题有效性”更为合理。</p>
<p>再以员工管理用例图中的“提交工作日志”为例。企业的内部术语为“日志报工”若以统一语言的角度讲似以“日志报工”用例名为佳。然而在英文中并无“报工”的恰当翻译更为人接受的英文名为“Submit Work Log”因而用例还是应命名为“提交工作日志”。</p>
<p>显然,通过对用例的不断打磨,对存有疑惑的用例,通过可视化的用例图与领域专家不断沟通,借助用例规格的设计指导,可以帮助我们发现问题,并进一步挖掘出准确的领域术语,建立系统的统一语言,并为后续识别限界上下文以及领域建模奠定基础。</p>
<h3>识别用例的主题边界</h3>
<p>在绘制用例图时除了参与者、用例以及用例之间的关系外还有一个非常重要的要素主题边界Subject Boundary<strong>主题边界</strong>包含了一组高内聚的用例并需要设计者为这个边界确定一个主题Subject。显然主题的确定恰好就是对用例的归类至于归类的原则正是[第 3-4 课 识别限界上下文]中提及的两个方面:</p>
<ul>
<li>语义相关性</li>
<li>功能相关性</li>
</ul>
<h4>语义相关性</h4>
<p>通过语义相关性来判别用例是否存在高内聚是一种业务分析手段。就好像我们整理房间一般相同类别的物品会整理放在一处例如衣服类鞋子类书籍类……每个类别其实就是所谓的“主题Subject”。在前面识别用例时我就要求针对用例名要字斟句酌。用例名通常为动宾短语宾语往往体现了领域概念。显然在用例名中如果包含了相同领域概念就可以认为是语义相关的就可能归类到同一个主题中。</p>
<p>在识别用例的主题边界时我抛开了用例图的约束选择将用例图直接以主题边界进行划分不再继续保留参与者与用例、以及用例之间的关系。如下图是对合同Contract主题的识别</p>
<p><img src="assets/e4a8e880-cae0-11e8-852c-83bf566b6761" alt="img" /></p>
<p>这种对用例的可视化方式可以认为是用例图的另一种视图,即“主题视图”,主要表现用例的分类和相关性,属于领域场景分析中的 Where 要素;而之前给出的用例图则为<strong>参与者视图</strong>,表现了参与者、用例之间的协作关系,属于领域场景分析中的 Who、Why 与 What 要素。两种不同的用例视图可以提供不同的参考价值,同时又保障了用例可视化的清晰度。</p>
<p>仔细分析合同的主题视图我们发现在这个主题边界中的所有用例用例名都包含了“合同Contract”这个领域概念这就是所谓的“语义相关性”。有时候这种语义相关性并没有这么直接需要就领域概念的共同特征进行归纳例如市场Marketing主题</p>
<p><img src="assets/0263d6f0-cae1-11e8-b235-ab9d56e6ad7c" alt="img" /></p>
<p>在这个主题中,包括了市场需求、需求订单、客户需求等领域概念,我们却不能分别为其建立主题,毕竟这样建立的主题太过散乱而细碎。这时,就需要针对领域概念,建立抽象,即<strong>寻找这些领域概念的共同特征</strong>。显然,无论是市场需求,还是经过评估后形成的订单及客户需求,都是为一种更高的抽象层次“市场”服务的。</p>
<p>通过语义相关性判断用例的归属时,一个用例有可能包含两个语义,这时就需要判断语义与主题相关性的强弱。例如,“从储备人才转为正式员工”用例,究竟属于储备人才主题,还是员工主题?判断语义的相关性强弱时,可以依据用例的业务价值或用户目标,应优先考虑满足用户目标的语义。显然,“从储备人才转为正式员工”用例的用户目标是生成员工记录,储备人才的信息仅仅作为该领域行为的输入,答案不言而喻。</p>
<h4>功能相关性</h4>
<p>领域概念是名词,而用例则是动词,表达了一种领域行为。在确定用例的主题边界时,如果我们发现一些用例虽然在领域概念上没有明显的语义相关性,但<strong>它们却服务于一个共同的用户目标或业务价值</strong>,则说明它们是<strong>功能相关</strong>的。例如考勤Attendance主题</p>
<p><img src="assets/1d7af590-cae1-11e8-8854-ffb1d914e591" alt="img" /></p>
<p><strong>功能相关性</strong>还体现于用例之间的关联与依赖,在用例图中,主要以用例关系的包含、扩展与泛化来体现。例如,人事专员用例图:</p>
<p><img src="assets/3801bb10-cae1-11e8-852c-83bf566b6761" alt="enter image description here" /></p>
<p>与员工管理功能相关的子用例包括:</p>
<ul>
<li>上传员工劳务合同</li>
<li>从储备人才转为正式员工</li>
<li>录入项目经历</li>
<li>录入技能信息</li>
<li>录入语言能力</li>
</ul>
<p>那么,员工主题就应该包含以上功能相关的用例:</p>
<p><img src="assets/48f04b80-cae1-11e8-b235-ab9d56e6ad7c" alt="img" /></p>
<p>在确定用例之间关系时,我提到了包含与扩展不同的“粘度”。在确定功能相关性时,尤其要特别关注主用例的扩展用例。根据我的经验,大多数扩展用例提供了不同于主业务视角的关注点,而这些关注点往往在支撑子领域场景中提供了共同的业务价值,可以对它们做进一步抽象。在 EAS 中这样的扩展用例主要体现在两个方面文件上传与下载、消息通知故而可以为其分别建立主题边界文件共享File Sharing与通知Notification</p>
<p><img src="assets/64e40e80-cae1-11e8-b235-ab9d56e6ad7c" alt="enter image description here" /></p>
<p>注意文件共享与通知主题中的用例名,描述了与具体领域场景无关的通用业务。例如,在招聘专员用例图中,定义了“上传储备人才简历”和“通知招聘计划审核人”两个用例,它们实际上分别对应了“上传 Word 文件”与“发送通知电子邮件”、“发送站内信息”用例。之所以这样描述用例名,是因为这两个主题可能会为通过其他主题提供业务支撑,一旦具体化,就无法满足通用要求。</p>
<p>文件共享和通知主题中的用例并没有出现在之前识别的用例图中。通过参与者识别用例图时,我们是根据先启阶段识别出来的核心业务流程、史诗级故事与主故事,并通过设想参与者参与的领域场景,进而驱动得出这些用例。文件共享和通知主题中的用例则是通过寻找所有的扩展用例,进而归纳出它们的共同特征。这是两种迥然不同的用例分析方法。</p>
<h4>设计的决策</h4>
<p>无论是寻找领域概念的共同特征,还是识别用例行为的用户目标,都需要一种抽象能力。在进行抽象时,可能出现“向左走还是向右走”的困惑。这是因为抽象的层次可能不同,抽象的方向或依据亦有所不同,这时就需要做出<strong>设计上的决策</strong>。例如针对用例中识别出来的“员工”与“储备人才”领域概念我们可以抽象出“人才”的共同特征从而得到人才Talent主题</p>
<p><img src="assets/7c51ad20-cae1-11e8-852c-83bf566b6761" alt="img" /></p>
<p>然而从共同的用户目标考虑储备人才又是服务于招聘和面试的似乎归入招聘Recruiting主题才是合理的选择</p>
<p><img src="assets/946ca2c0-cae1-11e8-8854-ffb1d914e591" alt="img" /></p>
<p>实际上还有第三种选择就是将储备人才单独抽离出来形成自己的“储备人才Candidate”主题</p>
<p><img src="assets/aaf4c360-cae1-11e8-b235-ab9d56e6ad7c" alt="img" /></p>
<p>该如何抉择呢?我认为须得思考为什么要识别主题边界?显然,这里识别的主题边界仅仅是设计过程中的中间产物,并非我们最终的设计目标。主题边界是对用例的分类,在用例图中体现了用例的边界,而这种边界恰好可以对应领域模型的限界上下文,并为设计模型的包、模块提供设计指导。因此,究竟为人才主题,还是招聘主题,或者单独的储备人才主题,完全可以从限界上下文或者领域建模的角度去思考。</p>
<h4>主题边界体现用例的内聚性</h4>
<p><strong>主题边界并不以边界内用例的多寡为设计准则。<strong>至少在进行领域场景分析时,不要因为一个主题边界包含了太多的用例,就人为地对其进行更细粒度的拆分,关键还是要</strong>考察用例的内聚性</strong>。例如与项目管理有关的主题包含的用例数量就非常不均匀。项目Project主题包含的用例为</p>
<p><img src="assets/ae9c2b10-cae2-11e8-b235-ab9d56e6ad7c" alt="img" /></p>
<p>问题Issue主题包含的用例为</p>
<p><img src="assets/c5bfa150-cae2-11e8-b235-ab9d56e6ad7c" alt="img" /></p>
<p>项目成员Project Member主题包含的用例最少</p>
<p><img src="assets/dd8c4fe0-cae2-11e8-83b7-256b708eb91a" alt="enter image description here" /></p>
<p>识别主题边界不是求平衡,更不是为了让设计的模型更加好看,它的设计质量可能会直接影响到后续的限界上下文识别。或许内聚性的识别需要较强的分析能力和抽象能力,但只要我们遵循领域场景分析的设计思想,按部就班地通过业务流程识别参与者,再根据参与者驱动出清晰表达的用例图,最后再根据语义相关性和功能相关性识别主题边界,就能获得一个相对不错的场景分析结果。毕竟,这个分析过程是有章可循的,在知识的积累上也是层层递进的。整个过程不需要任何与技术实现有关的知识,非常利于领域专家与团队的共同协作和交流。</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/领域驱动设计实践(完)/031 实践 先启阶段的领域场景分析(上).md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/领域驱动设计实践(完)/033 实践 识别限界上下文.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":"70997e3c7ca03cfa","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>