learn.lianglianglee.com/专栏/领域驱动设计实践(完)/058 彩色 UML 与彩色建模.md.html
2022-05-11 19:04:14 +08:00

1133 lines
50 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>058 彩色 UML 与彩色建模.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 class="current-tab" 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>058 彩色 UML 与彩色建模</h1>
<p>如果某个领域已经形成了稳定的分析模式在设计该领域的分析模型时这些模式就可以提供有价值的参考。可惜分析模式需要有人来总结和提炼最好的分析模式提炼者需要兼具领域知识和软件建模能力。很早以前Martin Fowler 扮演了这一角色,他贡献了《分析模式》这本经典的著作。这是公开的分析模式。囿于领域知识的壁垒以及商业竞争的压力,各个领域或许已经通过数年的演化获得了稳定的分析模式,却只能像传说那样仅限于口传而不见诸于文字。这不能不说是一种遗憾。软件的发展是靠着各种<strong>重用</strong>才变得如此快速高效,领域模型却因为缺乏可以重用的模式,变得有些举步维艰。</p>
<p>如果没有这种可以直接参考甚至重用的模式,那就必须回到方法与过程上来。分析模式将模型分为知识级和操作级,就是一种建模方法,只可惜这种方法太过艰深晦涩,不曾提供清晰直观的建模思路。名词动词法又过于简单而随意,欠缺精准,属于分析建模的初级阶段。倘若要面向一个非常复杂的领域,各种领域概念如混乱的思绪一般纷至沓来,就需要运用更加系统更有条理的建模方法。</p>
<p>Peter Coad 等人提出的彩色 UMLColor UML应运而生。个人认为它为领域建模做出了两大贡献。</p>
<p>第一个贡献是它定义了四种与领域无关的模型架构型Archetype时标架构型、角色架构型、描述架构型和参与方—地点—物品架构型。这四种架构型基本上涵盖了所有能够表达领域概念的模型对象。在领域建模时可以利用这些架构型提供的特征描述帮助我们识别对象。这恰好是领域驱动设计缺失的内容。</p>
<p>第二个贡献是它创新地引入了颜色标记对领域模型进行可视化展示,丰富了模型的表现力,而这种彩色建模的手段也能够有效地促进领域专家与开发团队之间的交流。当颜色成为模型对象的一种重要编码时,结合它归纳的四种架构型,就能够清晰传递各个对象在业务场景中扮演的角色,理清它们之间的关系。倘若再摆脱 UML 对模型的约束,就可以用各种颜色的即时贴来表示领域模型,将彩色 UML 的思想运用到可视化协作的建模过程中。</p>
<h3>彩色 UML 的架构型</h3>
<p>色彩是表象,架构型才是彩色建模方法的重心。若要学会运用彩色建模,就需要明确这些架构型的特征,方才可以依照这些特征在浩如烟海的领域语言中按图索骥。</p>
<h4>时标架构型</h4>
<p>时标架构型Moment-interval Archetype的核心要素是时刻或时段。《彩色 UML 建模》一书这样总结时标架构型:</p>
<blockquote>
<p>它代表了出于商业和法律上的原因,我们需要处理并跟踪的某些事情,这些事情是在某个时刻或某一段时间内发生的。……(它)寻找的是问题域中具有重要意义的时刻或时段。</p>
</blockquote>
<p>显然,时刻或时段是时标架构型的特征属性。在这个时刻或时段,有某件事情发生了,而这件事情对于我们要处理的领域而言,具有重要意义。如果缺少对它的记录,就会影响到商业的运营和管理,或者引起法律上的纠纷。一言以蔽之,时标架构型的核心要素包括:<strong>时刻/时段</strong><strong>重大事件</strong>,二者缺一不可。例如,一次销售发生在某一个时刻,如果缺少对销售的记录,会影响企业的收支估算,影响销售人员的提成收益。一次租赁从登记入住到租约期满,发生在一个时段,如果缺少对租赁的记录,会导致租赁双方的法律合同纠纷。</p>
<p>时刻或时段是业务含义的属性而非技术因素。例如我们不能将数据记录的创建或修改时间戳看作时标架构型的时标属性。即使是业务含义的属性也不能说明具有时标属性的对象就一定是时标型对象。例如一个员工具有出生日期属性但员工的诞生对于一个企业而言没有任何意义。如果要更好地管理员工更为关注的是员工的入职日期OnBoardingDate。那么此时的员工会是时标型对象吗——不是因为员工这个对象并非入职日期这个时刻发生的事件入职 OnBoarding 对象才是。</p>
<p>因此,寻找时标型对象就是要从业务流程中找到任何一个重大的时刻或时段,然后再来分析在这个时刻或时段到底发生了什么,发生的事情结果就是我们要寻找的建模目标。例如,在 2019 年 6 月 7 日 9 时……</p>
<ul>
<li>考生张华踏入了高考语文的考场发生了一次考试Exam</li>
<li>学生刘烨在图书馆借阅了一本《领域驱动设计》产生了一次借阅Borrowing</li>
<li>客户李明取走了一笔 3000 元人民币的款项产生了一次取款交易Transaction</li>
<li>交警李飞处理了一起交通违法事项开具了一次罚单Ticket</li>
<li>买家唐嫣在淘宝购买了一支护手霜提交了一次订单Order</li>
</ul>
<p>以上事件都发生在 2019 年 6 月 7 日 9 时,产生的记录在各自系统中都具有不可缺失的重要意义。缺少考生考试记录,会影响考生成绩;少了一次借阅,可能丢失一本书;交易记录如果存储失败,会影响银行的对账;少记录一次处罚,会给公务人员中饱私囊的机会;订单如果找不到了,买家和卖家就会产生纠纷。显然,这些时标型对象都会影响到运营和管理。</p>
<h4>角色架构型</h4>
<p>角色架构型Role Archetype是一种参与的方式它由参与方、地点或物品来承担。角色就好像是参与方—地点—物品模型对象戴的帽子这些对象在不同的业务场景中扮演了不同的角色参与了业务的协作。因此角色架构型接近于面向对象设计中的角色接口参与方—地点—物品作为对象实现了这些角色接口。例如在银行的转账场景中账户 Account 是参与方对象,它扮演的角色是转出方 SourceAccount 和转入方 DestinationAccount 角色。</p>
<p>除了参与协作,角色架构型有时候也表达了某种关系。例如,角色对象 ProductOrdered 体现了订单项与产品之间的关系,在订单场景中,它扮演了订购产品的角色;角色对象 MaterialShipped 体现了配送单与物料之间的关系,在配送场景中,它扮演了已发货物料的角色。</p>
<p>如果扮演角色的对象是参与方,则角色架构型往往代表业务领域中形形色色的职业角色,如销售人员 SalePerson、供应商 Supplier、收银员 Cashier、团队成员 TeamMember、订单拥有者 OrderOwner。</p>
<h4>描述架构型</h4>
<p>Peter Coad 认为描述架构型Description Archetype是一种类似分类目录catalog-entry-like的描述。这个定义比较含糊。我的理解是描述对象包含的属性体现了<strong>分类的特征</strong>是对描述目标对象的扩展与增强。例如一本书的属性包括书的书名、作者、出版社、出版日期、ISBN 号与价格,而书的推荐语、作者简介、版权信息与目录就是对书的扩展描述,且这些属性也说明了这是一本书,而非家电、化妆品、衣物等类别。</p>
<p>虽然 Peter Coad 没有明确指出描述架构型是针对哪一种架构型的描述,但通常可以认为是针对参与方—地点—物品架构型对象的增强。</p>
<h4>参与方—地点—物品架构型</h4>
<p>参与方—地点—物品架构型Party-place-thing Archetype直接说明了三种类型的模型对象即参与方、地点与物品其中参与方指代人或组织。由于参与方、地点与物品的英文首字母分别为 PPT我们往往将其戏称为 PPT 对象。</p>
<p>与时标型对象相似PPT 对象具有非常明显的特征。在需求描述中,只要发现领域概念与人、组织机构、地点或物品相关,就可以识别为 PPT 对象。例如雇员 Employee 和买家 Buyer 可以作为员工管理场景和电商购买场景的参与方。组织机构也可以作为参与方,如购买团体保险的组织 Organization、物流快递的承运商 Carrier。物流快递的配送站 DistributionStation、通信领域中的基站 NodeB 则体现了地点的概念。体现物品概念的模型对象更为常见,例如物料 Material、商品 Product、信用卡 CreditCard 以及前面提到的书 Book都是物品对象。</p>
<p><strong>注意</strong>:作为 PPT 构造型的组织机构以及作为角色构造型的角色,与权限认证管理场景中的组织机构与角色属于两个不同的模型概念。二者的区分可以从限界上下文的角度来考虑。例如,团体保险的组织 Organization 是客户上下文中的领域对象,角色对象 SalePerson 是销售上下文中的领域对象,而权限认证管理中的 Department、Role 等概念,则都属于权限认证上下文。</p>
<h4>架构型对领域分析建模的启发</h4>
<p>彩色 UML 定义的这四种架构型并不是要限制我们的模型对象而是希望通过提炼模型对象的特征来帮助建模人员。例如时标型对象的特征是时刻与时段这就促使我们去需求描述中寻找那些具有时刻时段特性的模型对象。PPT 对象归纳了参与方、地点与物品这三种类型,就能启发建模人员去寻找包括人和组织机构的参与方、地点和物品。角色架构型的特征体现为业务场景的参与角色,在分析业务场景时,我们就会有意识地识别各种模型对象所承担的角色(帽子)到底是什么。描述架构型作为 PPT 对象的补充与扩展,既可以让我们设计出粒度合理的 PPT 对象,又不至于丢失一些非核心但却能起到补充作用的领域对象。</p>
<p>Peter Coad 等人认为:由这四种架构型构成了领域无关的组件。在分析模型中,我们也可以通过显式地标记架构型或者通过颜色来体现这些架构型对象,但在领域设计模型和领域实现模型中,我们其实并不是特别看重领域对象到底是什么架构型。换言之,彩色 UML 定义的四种架构型并非领域建模的目标,而是领域分析建模活动中的一种方法。在领域分析建模活动中,不必执着或拘泥于确定该模型对象到底属于哪一种架构型。只要你识别出了合理的领域对象,就达到目标了。如果总是花费时间纠结它到底是 PPT 架构型还是时标架构型,就会让你忘记什么才是建模的初衷,舍本逐末。</p>
<h3>彩色 UML 的建模过程</h3>
<p>彩色 UML 并没有给出识别领域模型对象的分析过程。它假定我们在寻找到一个类时,需要利用一个检查清单来确定它属于什么架构型,从而确定该类在 UML 类图与时序图中的颜色。这个检查清单如下:</p>
<ol>
<li>它是时刻或时段,是出于业务原因或法律原因,是系统需要追踪的东西吗?如果是这样,那么它是粉红色的时刻时段。</li>
<li>否则,它是一个角色吗?如果是这样,那么它是黄色的角色。</li>
<li>否则,它是一个分类目录条目似的描述,包含了一组可以反复应用的值吗?如果是这样,那么它是蓝色的描述。</li>
<li>否则,它就是参与方—地点—物品。它是绿色的参与方—地点—物品(绿色是默认色,如果不是粉红色、黄色或蓝色,它就是绿色)。</li>
</ol>
<p>这是针对<strong>单个模型对象</strong>的检查过程。我们识别出一个模型对象,然后讨论它是什么架构型,确定了架构型后,在 UML 类图中绘制出来,并以对应的颜色作为背景色,然后继续识别下一个模型对象。——这是我们希望看到的建模过程吗?</p>
<p>我认为这种方式实际上降低了彩色 UML 的价值,将它沦落为一种具有彩色标记的 UML 表示法。采用这种方式建模,意味着在运用彩色 UML 之前,我们已经识别出了各种领域模型对象,彩色 UML 的作用仅仅在于确定它们的架构型,然后为其涂上美丽的颜色。可是,如果已经有了这些领域模型对象,我们还要彩色 UML 干什么呢?难道彩色 UML 的目的只是为了给类涂上颜色,让 UML 变得更加好看吗?</p>
<p>领域建模过程显然不是这样的!彩色 UML 带来的价值绝不仅止于此,可惜它的创造者 Peter Coad 等人反倒是低估了这一方法。这不能不说是一种遗憾。通读《彩色 UML 建模》全书,我认为作者希望仿照 Martin Fowler 的《分析模式》,以彩色 UML 的形式为领域建模活动提供可以参考的领域模型(即书中列出的 61 个领域相关的组件)。因此,它过于强调获得的领域模型,却不曾清晰地表达是如何获得这些领域模型的。吊诡的事情在于,其实后者才是领域建模的关键,也是领域建模的难点所在。</p>
<p>我们可以看看该书第 3 章给出的案例“产品销售管理”,了解它的领域模型在彩色 UML 的框架下是如何得来的,即可印证我的评价是否中肯。书中讨论的产品销售管理主要针对客户开发票与支付的业务功能。书中分析该业务场景,获得如下业务步骤:</p>
<ul>
<li>定义产品类型和产品</li>
<li>销售给客户</li>
<li>发送产品</li>
<li>给客户开发票</li>
<li>记录产品的支付,追踪并解决交付问题报告</li>
<li>达成协议并完成评估</li>
</ul>
<p>产品销售管理还要与库存管理交互,需要在发送产品之后从库存中扣除数量;同时,还需要与会计管理交互,针对发票金额进行过账。</p>
<p>在描述了产品销售管理的业务背景与关联系统之后,书中直接给出了如下时标型对象。至于它们是如何获得的,书中完全不曾提及:</p>
<ul>
<li>产品价格ProductPrice</li>
<li>对客户的销售SaleToCustomer</li>
<li>发货给客户ShipmentToCustomer</li>
<li>交付给客户DeliveryToCustomer</li>
<li>交付问题报告DeliveryProblemReport</li>
<li>给客户开发票InvoiceToCustomer</li>
<li>折扣协议DiscountAgreement</li>
<li>佣金协议CommissionAgreement</li>
<li>费用和开销分配CostAndOverheadAllocation</li>
<li>市场调研MarketingStudy</li>
<li>销售预测SaleForcast</li>
<li>地理区域指派GeographicRegionAssignment</li>
</ul>
<p>下图是这些时标型对象之间的关系:</p>
<p><img src="assets/ddf6fbb0-b128-11e9-90ed-85df496a282b" alt="img" /></p>
<p>接下来,作者摘取了一个交互场景“计算销售代表的直接佣金”,以此说明四种架构型对象之间的交互关系。书中这样描述:</p>
<blockquote>
<p>消息发送者要求黄色的“销售代表”计算它的佣金,即来自于它们自己的销售的佣金(称为“直接”佣金)。“销售代表”对象要求它的每个粉红色的“销售”构建一份“产品销售明细”列表。接下来,“销售代表”对象要求它的每个粉红色的“佣金”对象计算直接佣金。然后,“佣金”对象将它的产品描述和数量与销售明细进行匹配,寻找有效的匹配,再计算产品销售的佣金。某些“佣金”对象可能没有链接到“产品描述”,在这种情况下,佣金适用于所有的产品销售明细。最后,“销售代表”将结果返回给消息发送者。</p>
</blockquote>
<p>这个交互的时序图如下所示:</p>
<p><img src="assets/06415070-b129-11e9-90ed-85df496a282b" alt="58906585.png" /></p>
<p>在“计算销售代表的直接佣金”交互场景中,真正对外的交互起点其实是黄色的角色对象:销售代表 SalesRep。因为需要计算销售的佣金就需要获得销售明细与佣金协议故而引入了两个时标型对象 SaleToCustomer 与 CommissionAggrement。为了支持佣金计算的功能佣金自身也需要提供明细信息于是得到了 CommissionAgreementDetail 对象。注意,这个对象并非描述对象,而是一个时标型对象。</p>
<p>接下来,作者就开始针对产品组件进行进一步的模型细化工作。在这个过程中,作者讨论了 PPT 对象与描述对象之间的差异:</p>
<blockquote>
<p>绿色的“产品”是业务所销售的东西,是可以单独标识的(它有序列号),是必须单独追踪的。如果一件产品不是可以单独标识的,您就不需要一个绿色的物品;相反,您可以用带数量的、类似产品目录项的蓝色描述。</p>
</blockquote>
<p>这说明区分 PPT 对象与描述对象的一个重要特征是看它是否需要单独标识。这与领域驱动设计区分实体和值对象有着异曲同工之妙。</p>
<p>这里虽然提到产品需要“单独追踪”,但由于它没有时标特性,因而被建模为 PPT 对象;与之相反,产品价格则是时标型对象,因为它的数量和计价单位适用于一个时段。针对产品价格的建模,作者给出了非常具有参考价值的评述,有利于我们理解时标型对象的特征:</p>
<blockquote>
<p>对于价格,你可以有几种建模选择。您可以将它作为绿色“产品”的一个属性(例如,红色法拉利的价格,岿然不动的价格!),或者是蓝色的描述(例如,某个尺寸的 Snickers 巧克力棒的价格)。但是,如果您希望<strong>追踪</strong>以往的价格(用于趋势分析),当前的价格(用于销售),以及未来的价格(用于计划将来的价格变更),那么您就需要将“产品价格”建模为一个粉红色的时刻时段。</p>
</blockquote>
<p>在将产品价格建模为时标型对象时,还将它链接到了一个黄色的“定价人 Pricer”角色对象该角色负责设定价格。产品作为 PPT 对象中的物品对象,在产品销售管理中,会因为销售的产生,而扮演“销售的产品 ProductBeingSold”这个角色它实际体现了产品和销售之间的关系。因此围绕着产品获得的彩色模型如下</p>
<p><img src="assets/2790b040-b129-11e9-be4b-8fba8dbeacaa" alt="img" /></p>
<p>由于篇幅有限,我并没有将《彩色 UML 建模》书中的整个例子全部摘抄过来,否则,我就成了一名彻头彻尾的文抄公了。本节内容仅呈现了彩色 UML 的冰山一角,但结合该案例对建模过程的简述,大致可以总结出这种建模思想的不足之处:</p>
<ul>
<li>彩色 UML 以时标型对象为主要的建模核心,但对于如何寻找这些时标型对象,除了谈到它的特征之外,并没有给出清晰的思路。</li>
<li>建模时需要结合具体的业务场景考虑各种架构型对象之间的交互,交互场景的发起者为“消息发送者”,但提供对外交互接口对象的构造型却并不确定,可以是这四种架构型中的任何一种。</li>
<li>描述对象虽然是 PPT 对象的补充与增强,但它并不一定成为 PPT 对象的附庸,有时候甚至会直接暴露行为给“消息发送者”。</li>
<li>彩色 UML 并没有限定这四种架构型对象之间的关系,彼此之间可以互相链接,没有明确统一的关系约束。</li>
<li>除了确定时标型对象作为建模起点之外,并没有为其他架构型对象给出清晰的建模步骤。</li>
</ul>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/领域驱动设计实践(完)/057 精炼领域分析模型.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/领域驱动设计实践(完)/059 四色建模法.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":"70997e7ceda73cfa","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>