learn.lianglianglee.com/专栏/领域驱动设计实践(完)/049 理解领域模型.md.html
2022-05-11 18:59:25 +08:00

887 lines
43 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>049 理解领域模型.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 class="current-tab" 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>049 理解领域模型</h1>
<p>我始终认为Eric Evans 的领域驱动设计是对软件设计领域的一次重新审视是在面向对象语言大行其道时对数据建模的“拨乱反正”。Eric 强调了模型的重要性,例如他在书中总结了模型在领域驱动设计中的作用包括:</p>
<ul>
<li>模型和设计的核心互相影响</li>
<li>模型是团队所有成员使用的统一语言的中枢</li>
<li>模型是浓缩的知识</li>
</ul>
<p>显然,模型在领域驱动设计中是设计的起点和关键。但是,该<strong>如何才能得到</strong>我们心目中能够准确表达业务需求的模型呢?</p>
<p>我们需要认识到模型和领域模型是两个不同层次的概念。如前所述,模型还可以是数据模型或服务模型,这取决于我们观察现实世界业务需求的视角。因此,<strong>领域模型是以“领域”为关注核心的模型,是对领域知识严格的组织且有选择的抽象。</strong></p>
<h3>领域模型的特征与分类</h3>
<p>即便有了这个定义,却没有清晰地说明领域模型到底长什么样子。领域模型究竟是什么呢?是使用建模工具绘制出来的 UML 图?是通过编程语言实现的代码?或者干脆就是一个完整的书面设计文档?</p>
<p>我认为UML 图、代码与文档仅仅是表达领域模型的一种载体而已,如果绘制出来的 UML 图或者编写的代码与文档并没有传递领域知识,那就不是领域模型。</p>
<p>因此,领域模型应该具备以下特征:</p>
<ul>
<li>运用了统一语言来表达领域中的概念</li>
<li>蕴含了业务活动和规则等领域知识</li>
<li>对领域知识进行了适度的提炼和抽象</li>
<li>它的建立是一个迭代的演进的过程</li>
<li>能够有助于业务人员与技术人员的交流</li>
</ul>
<p>既然如此,不管领域模型的表现形式,只要它正确地传递了领域知识,并有助于业务人员与技术人员的交流,就可以说是领域模型。这是一个更不容易犯错误的定义。它其实体现的是一种建模原则。很可惜,这样高屋建瓴的原则并不能指导开发团队运用领域驱动设计。就好似软件设计有个核心原则是“高内聚低耦合”,然而知道这个原则并不能保证你设计出高内聚低耦合的方案。故而诸如这样打太极似的原则与模糊定义,并不能让开发团队满意,他们还是会执着地追问:领域模型<strong>到底</strong>是什么?</p>
<p>Eric 并没有就此作出正面地解答,但是他在模型驱动设计中提到了模型与程序设计之间的关系:</p>
<blockquote>
<p>“模型驱动设计不再将分析模型和程序设计分离开,而是寻求一种能够满足这两方面需求的单一模型。”</p>
</blockquote>
<p>这句话说明分析模型和程序设计应该一起被放入到同一个模型中。这个单一模型就是“领域模型”。他反复强调程序设计与程序实现应该忠实地反映领域模型,他写道:</p>
<blockquote>
<p>“软件系统各个部分的设计应该忠实地反映领域模型,以便体现出这二者之间的明确对应关系。”</p>
</blockquote>
<p>同时,他还要求:</p>
<blockquote>
<p>“从模型中获取用于程序设计和基本职责分配的术语。让程序代码成为模型的表达。”</p>
</blockquote>
<p>在我看来,设计对领域模型的反映,就是“领域设计模型”;代码对领域模型的表达,就是“领域实现模型”。领域分析模型、领域设计模型与领域实现模型在领域视角下,成为了领域模型中相互引用和参考的不可或缺的组成部分,它们分别是分析建模活动、设计建模活动与实现建模活动的产物。</p>
<p>模型驱动设计非常强调模型的一致性Eric Evans 甚至认为:</p>
<blockquote>
<p>“将分析、建模、设计和编程工作过度分离会对模型驱动设计产生不良影响。”</p>
</blockquote>
<p>这正是我将分析、设计和实现都统一到模型驱动设计中的原因。因此,倘若我们围绕着“领域”为核心进行设计,采用的就是领域模型驱动设计,整个领域模型就应该包含领域分析模型、领域设计模型和领域实现模型:</p>
<p><img src="assets/3fce7ce0-9b40-11e9-bc97-93abff33030c" alt="51525288.png" /></p>
<h3>如何表现领域模型</h3>
<p>因为交流的目标对象不同不同的领域模型会有不同的表现形式。文档描述、UML 图与实现代码是最为常见的模型表现形式。但是,这些表现形式仅仅是对领域建模结果的一种呈现。领域模型的目的在于交流,因此更好的方式是引入直观而又具备协作能力的<strong>可视化手段</strong>,引导领域专家和开发团队参与到领域建模的整个活动中来,而不是由专职的分析师或设计师使用冷冰冰的建模工具绘制 UML 图。通过使用各种颜色的便利贴、马克笔与白板纸等可视化工具,让彩色的领域模型成为一种沟通交流的视觉工具。领域模型中的领域概念、协作关系皆生动形象地活跃在彩色图形上,使得团队协作成为可能,让领域模型更加直观,从而避免沟通上的误差与分歧,使得团队能够迅速就领域模型达成一致。</p>
<p>例如,在运用用例图分析业务逻辑时,就可以用黄色便利贴代表参与者,蓝色便利贴代表主用例,绿色便利贴代表包含用例与扩展用例。便利贴可以在白板纸上自由移动,便于团队的协作和交流:</p>
<p><img src="assets/6dd5ef60-9b40-11e9-bc97-93abff33030c" alt="76339225.png" /></p>
<p>事件风暴更是将这种可视化手段用到了极致沿着一条时间线通过对事件、命令、读模型Read Model、流程、策略Policy的不断识别领域专家与开发团队一起探寻业务的真相绘制出表现业务流程与领域模型的设计画布</p>
<p><img src="assets/7c169de0-9b40-11e9-bb1e-07f6ac2e1b14" alt="73688849.png" /></p>
<p>职责驱动设计使用时序图来体现对象之间的协作关系。同样,我们可以用即时贴表达参与协作的对象,在白板上绘制出协作的时序图。如下图所示,我使用不同的颜色表达远程服务、应用服务、领域服务、资源库和聚合:</p>
<p><img src="assets/8e31c770-9b40-11e9-bb1e-07f6ac2e1b14" alt="48761851.png" /></p>
<p>图中的红色五角星表达一个业务场景只需一个对外公开的接口。多数情况下,这个对外公开的接口就是远程服务。在时序图上,对象之间以箭头表达消息的传递。红色箭头指向的对象,会履行该消息代表的职责,例如 exists() 职责就由该红色箭头指向的 TrainingRepository 对象承担。一个对象如有太多红色箭头指向它,就说明该对象可能承担了太多职责,属于设计的坏味道。同时,我们也需要注意发起消息箭头的对象,它通常代表某个方法的调用者。如果发出了太多消息,说明调用逻辑变得过于复杂,缺少必要的封装层次,同样属于设计的坏味道。图中绘制的蓝色圆圈代表了应用服务发出的调用消息。由于领域驱动设计不允许将业务逻辑封装到应用服务,因此,在一个时序图中应该只能有一个蓝色圆圈。</p>
<p>时序图自身的可视化特征,可以直观地体现职责分配是否平衡。例如,针对一个业务场景绘制的时序图如果过宽,则说明对象的粒度可能太细,增加了不必要的抽象与间接导致协作复杂度增加;如果时序图过窄而高,又可能说明对象的粒度可能太粗,协作仅在有限的几个对象之间完成,没有做到职责的分治。因此,这些可视化特征都能够传递信号,直观地呈现“设计坏味道”,以便于我们对其进行修改和调整。</p>
<p>领域建模的结果固然比过程重要,但如果缺乏高效沟通的建模手段,或许我们根本无法获得正确的领域模型。显然,可视化的表现形式与工作坊的沟通方式可以帮助我们在沟通交流时走出“盲人摸象”的窘境,在团队中传递知识,进而对整个业务系统的领域逻辑达成共识,最终形成领域分析模型与领域设计模型。</p>
<p>至于领域实现模型则可以通过协作编写测试开始。测试用例体现了具体的业务场景测试方法的命名更加接近自然语言Given-When-Then 模式与业务场景的描述非常契合,这就使得<strong>领域专家与开发人员结对编程</strong>成为了可能。如上一课给出的转账业务场景的测试方法,完全可以是这种协作的产物。在针对业务场景进行测试驱动开发时,可以让开发人员将注意力完全放在业务逻辑的实现上。由于代码仅仅是业务逻辑的表达,领域专家就有能力参与进来,帮助开发人员打磨代码,使得代码的编写满足统一语言的要求。代码即模型,这是领域模型最理想的表现形式,也是领域建模最终的模型产物。</p>
<h3>领域模型与统一语言的关系</h3>
<p>领域模型之所以被划分为三个模型,源于不同活动中的交流对象与交流重心各不相同。在分析建模活动中,开发团队与领域专家一起工作,通过建立更加准确而简洁的分析模型,直观地传递着不同角色对业务知识的理解。在设计建模活动中,必须基于领域分析模型对模型中的对象做出设计改进,考虑职责的合理分配与良好的协作,建立具有指导意义的设计模型。在实现建模活动中,代码必须是领域设计模型的忠实表现,意味着它其实也忠实表现了领域分析模型蕴含的领域知识。一言以蔽之,**让领域分析模型服务于开发团队与领域专家,领域设计模型服务于软件设计人员,领域代码模型服务于程序员。**三个模型各司其职,各取所需,它们又都属于领域模型。</p>
<p>在建模过程中,我们需要不断地从“统一语言”中汲取建模的营养,并<strong>通过“统一语言”来维护模型的一致性</strong>。当开发团队根据领域分析模型建立领域设计模型时,如果发现领域分析模型中的概念未能准确表达领域知识,又或者缺少了隐式概念,就需要调整领域分析模型,使得领域设计模型与领域分析模型保持一致。领域实现模型亦当如此。显然,统一语言为领域模型驱动设计提供了一致的领域概念,使得领域模型在整个软件开发阶段保持了同步:</p>
<p><img src="assets/f5e24610-9b40-11e9-bc97-93abff33030c" alt="81361973.png" /></p>
<h3>迭代建模</h3>
<p><strong>分析、设计与实现不是割裂开的三个阶段</strong>而是一个迭代建模Iteration Modeling过程中的三个建模活动。在战略设计阶段我们可以通过业务场景识别系统的限界上下文。无论是采用用例场景分析还是事件风暴对限界上下文展开识别都可以认为是一个自底向上的建模过程。在获得限界上下文的同时我们也获得了相对细化的用例或主故事与初步的领域分析模型。为了避免分析瘫痪Analysis Paralysis应将这个过程控制在两周到一个月左右的先启Inception阶段完成。</p>
<p>先启阶段结束后,就应该进入针对限界上下文开展领域模型驱动设计的迭代开发。在迭代开发过程中,我们可以根据用户故事结合分析模式与四色建模等手段,进一步细化领域分析模型,然后结合设计模式与设计要素,引入职责驱动设计获得领域设计模型,最后,结合业务场景与设计模型,推进测试驱动开发实践进行编码开发,以小步快速的“红—绿—重构”反馈环不断地改进代码质量和增量开发,快速交付高价值的可运行的功能:</p>
<p><img src="assets/1892bc80-9b41-11e9-bb1e-07f6ac2e1b14" alt="82473387.png" /></p>
<p>**说明:**迭代建模与本图参考了 Scott W. Ambler 敏捷建模的思想,参见链接:</p>
<blockquote>
<p><a href="http://agilemodeling.com/essays/iterationModeling.htm">http://agilemodeling.com/essays/iterationModeling.htm</a></p>
</blockquote>
<p>迭代建模与迭代的增量开发一脉相承。它避免了在建模过程尤其是分析建模活动中的分析瘫痪,也避免了在设计建模活动中的过度设计,同时还能通过增量快速地开发出新功能来及时获得反馈。获得的领域模型也随着增量开发而不断演化,并始终指导着设计与开发。迭代建模使得建模活动成为迭代开发中不可缺少的一个重要环节,但整个活动却是轻量的,有效地促进了团队成员的交流,符合 Kent Beck 提出的核心价值观——沟通、简单和灵活。</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/领域驱动设计实践(完)/048 领域实现模型.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/领域驱动设计实践(完)/050 领域模型与结构范式.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":"70997e6628d93cfa","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>