learn.lianglianglee.com/专栏/领域驱动设计实践(完)/039 数据分析模型.md.html
2022-05-11 19:04:14 +08:00

897 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>039 数据分析模型.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 class="current-tab" 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>039 数据分析模型</h1>
<p>在 Eric Evans 提出领域驱动设计之前,对企业系统的分析设计多数采用数据模型驱动设计。如前所述,这种数据模型驱动设计就是站在数据的建模视角,逐步开展分析、设计与实现的建模过程。通过对数据的正确建模,设计人员就可以根据模型建立数据字典。数据模型会定义数据结构与关系,有效地消除数据冗余,保证数据的高效访问。由于软件系统的业务功能归根结底是对信息的处理,由此建立的数据模型也可以通过某种编程手段来实现,满足业务需求。</p>
<h3>数据分析模型</h3>
<p>数据建模过程中的分析活动会通过理解客户需求寻找业务概念建立实体Entity。在数据模型中一个实体就是客户希望建立和存储的信息。这是一个抽象的逻辑概念位于数据模型的最高抽象层。一个实体不一定恰好对应一个数据表它甚至可能代表一个主题域。在识别实体的同时我们还需要初步确定实体之间的关系。由于这个过程与数据库细节完全无关因而称之为对数据的“概念建模”建立的模型称之为<strong>实体关系模型</strong></p>
<p>经过数十年对数据建模的丰富与完善这个领域已经出现了许多值得借鉴和重用的数据模型。其中Len Silverston 的著作《数据模型资源手册》是最重要的模型参考手册。他通过对行业业务的梳理,建立了包括人与组织、产品、产品订购、装运、工作计划、发票等各个主题的数据模型。在确定系统的实体时,这些已有的数据模型可以作为我们的重要参考。</p>
<p>当然,每个软件系统的业务需求必然有其特殊性,除了对已有数据模型的参考,也有一些数据建模方法帮助我们获得实体关系模型。例如通过引入不同的用户视图创建不同的实体关系模型。用户视图的差异取决于业务能力的差异,例如,财务人员的观察视图显然不同于市场人员的观察视角,看到的数据信息显然也有所不同。这就像盲人摸象一般,虽然每个视角得到的实体关系模型只是大象的一部分,然而将这些代表不同人员不同观点的实体关系模型组合起来,就能形成整体的实体关系模型。</p>
<p>实体关系模型是数据建模的开始,目的是让我们可以从一开始抛开对数据库实现细节的考虑,寻找那些表达业务知识的重要概念,并明确它们之间的关系。但对数据模型的分析并不会就此止步,我们必须在分析阶段对实体做进一步细化,形成具体的数据表,并定义表属性,确定数据表之间的真实关系。这时获得的分析模型称之为“数据项模型”。实体关系模型与数据项模型之间的关系如下图所示:</p>
<p><img src="assets/104503b0-7fb4-11e9-91b9-2513956f5ea9" alt="img" /></p>
<p>在数据建模过程中越早确定数据库的细节越有利于数据模型的稳定。当今的软件开发已经不是关系型数据库一统天下的时代。NoSQL 甚至 NewSQL 的诞生,让我们在选择持久化机制时有了更多选择。比较关系数据库和 NoSQL 数据库前者是严格扁平的结构化数据后者却是无样式的数据结构Schemaless Data Structures结构不同建立的数据模型自然就有了天壤之别。一旦根据数据模型创建了物理的数据表再调整数据模型变化的成本就太高了。因此究竟选择关系数据库还是 NoSQL 数据库,对确立数据项模型至关重要,我们需要分开讨论。</p>
<h4>关系数据库的数据项模型</h4>
<p>关系数据库体现了关系模型,形成了一种扁平的结构化数据,这就要求进一步规范数据表的粒度,将实体或主题域拆分为多个遵循数据库范式的数据表,明确一个数据表的主要属性。</p>
<p>数据库范式是面向数据的分析建模活动的一个关键约束。这些范式包括一范式1NF、二范式2NF、三范式3NF、BC 范式BCNF和四范式4NF。遵循这些范式可以保证数据表属性的原子性、避免数据冗余和传递依赖等。</p>
<p>例如在确定数据项时,该如何考虑避免数据冗余?这就需要合理地设计表以及表之间的关系。</p>
<p>假设一个公司的员工可能同时具有多个角色:运输科的张飞是科室的负责人,他又是供应科的客户,供应科会将运输的任务委托给他;同时,他还是一家大型超市的供应商,负责将货物运输给超市。显然,我们不能在一个数据库中为张飞创建三条冗余的数据记录。运输科主任、供应科客户和超市供应商都是张飞担任的角色,无论他担任了什么角色,他都是该公司的一名员工。</p>
<p>在创建数据模型时,应该将角色属性从员工剥离出去,分别形成数据表 <code>t_employee</code><code>t_role</code>;又因为员工和角色之间存在多对多的关系,需要引入一个关联表 <code>t_employee_roles</code>。这个数据模型如下图所示:</p>
<p><img src="assets/57bcb710-7fbe-11e9-ace0-ad297907c3be" alt="enter image description here" /></p>
<p>当数据模型出现多对多关系时,之所以要引入一个关联表,是因为多对多关系会引入数据表之间的<strong>交叉关联</strong>。这个数据项模型中的 <code>t_employee_role</code> 并无映射的业务概念,引入该表,纯粹是数据库实现细节对模型产生的影响。</p>
<p>有时候承载多对多关系的关联表也可以具有一些附加的属性这样的关联表往往代表了业务逻辑中的一个业务概念例如学生Student与课程Course之间的多对多关系可以用课表Curriculum关联表来表达。Curriculum 属于学习领域的业务概念,但同时它又能有效解除 Student 与 Course 之间的交叉关联。</p>
<p>有的数据建模者甚至建议针对一对多关系也建立关联表因为关联表的引入使得这种关系更容易维护。例如产品Product和图片Picture是一对多关系直接定义 <code>t_product</code><code>t_picture</code> 数据表即可,但如果引入 <code>t_product_picture</code> 关联表就可以在数据库层面更好地维护二者之间的关系。有时一对多关系体现了父—子关系例如订单Order与订单项OrderItem它们之间的一对多关系其实代表了“每个订单项必须是一个也只是一个订单的一部分”。</p>
<p>在确定数据项模型时还需要考虑访问关系数据库的性能特性从而决定数据的粒度与分割。通常需要考虑数据表的规范化避免设计出太多过小的包含少量数据的数据表。一个数据表的粒度较小就会导致程序在访问数据库时频繁地在多张小表之间跳转。这个访问过程既要存取数据又要存取索引以找到数据导致I/O的过度消耗影响到整体的性能。因此数据模型很少具有一对一关系即使现实世界的概念存在一对一关系也应该尽量通过规范化将两张表的数据组织在一起合到一个实体中。例如我们说一位员工有一个家庭电话号码和工作电话号码若站在领域概念角度就应该建模为拥有两个不同电话号码PhoneNumber的员工Employee对象</p>
<p><img src="assets/a1c42190-7fbe-11e9-8b24-8b44cf4ff051" alt="img" /></p>
<p>数据模型却不能这样建立,因为我们需要考虑分开两张表带来的 I/O 开销。虽然家庭电话号码和工作电话号码都是相同的 <code>PhoneNumber</code> 类型,但却属于两个不同的属性,将它们合并放到 <code>t_employee</code> 数据表,并不会破坏数据库范式。</p>
<p>当然,这种合并并非必然,有时候还需要考虑数据访问的频率。例如一个银行账户,账户地址、开户日期与余额都是规范化的,按理就应该合并到 <code>t_account</code> 物理表中。但是,余额与其他两项属性的访问频率差异极大,为了使 I/O 效率更高,数据的存储更加紧凑,就应该将规范化的表分解为两个独立的表:</p>
<p><img src="assets/5faa61b0-7fc4-11e9-ace0-ad297907c3be" alt="enter image description here" /></p>
<h4>NoSQL 的数据项模型</h4>
<p>如果数据库选择了 NoSQL数据项模型会有所不同。由于 NoSQL 数据库是一种无样式的数据结构Schemaless Data Structures这使得它对数据项模型的约束是最少的。诸如 MongoDB、Elasticsearch 这样的 NoSQL 数据库,它所存储的 JSON 文档,可以在属性中进行任意嵌套,形成一种能够自由存取的文档结构。所以 Martin Fowler 又将这样的 NoSQL 数据库称之为“文档型数据库”。</p>
<p>当然,即使是没有样式的 NoSQL也无法做到随心所欲地建立数据模型尤其针对表之间的关系同样要受到实现机制的约束。例如在 MongoDB 中,可以选择使用 Link 或 Embedded 来维护关联关系,这时就需要结合具体业务场景来选择正确的关联关系。</p>
<p>假设我们要开发一个任务跟踪系统,需要能够查询分配给员工的任务。采用 Embedded 方式Employee 数据模型如下所示:</p>
<pre><code class="language-json">{
name: 'Kate Monster',
ssn: '123-456-7890',
role: 'Manager',
tasks : [
{ number: '1234', name: 'Prepare MongoDB environment', dueDate: '2019-01-15' },
{ number: '1235', name: 'Import Test Data', dueDate: '2019-02-15' },
]
}
</code></pre>
<p>如果需要查询员工的任务信息,就可以直接获得内嵌在员工内部的任务数组,无需执行多次查询。这时,选择 Embedded 就是合理的。倘若需要支持如下功能:</p>
<ul>
<li>显示所有明天到期的任务</li>
<li>显示所有未完成的任务</li>
</ul>
<p>显然,这两个功能要查询的任务与员工无关,而采用 Embedded 方式建立的数据模型却明确地表达了 Employee 与 Task 之间的父子关系,反而为任务的查询制造了障碍。倘若改用 Link 方式来建立二者之间的关联,情况就完全不同了:</p>
<pre><code class="language-json">//Tasks
[
{
_id: ObjectID('AAAA'),
number: 1234,
name: 'Prepare MongoDB environment',
dueDate: '2017-01-15'
},
{
_id: ObjectID('BBBB'),
number: 1235,
name: 'Import Test Data',
dueDate: '2017-02-15'
},
]
//Employees
{
_id: ObjectID('E00001'),
name: 'Kate Monster',
role: 'Manager',
tasks : [
ObjectID('AAAA'),
ObjectID('BBBB')
]
}
</code></pre>
<p>通过 Link 建立的数据模型相当于关系数据库建立的主外键关系,去掉了嵌套关系,任务可以被独立查询,如前所述的功能就变得格外简单。但调整后的数据模型又不利于支持查询员工任务的场景了,它会因为关联的原因导致执行两次查询。</p>
<p>选择 Embedded 或 Link 不仅会影响执行效率和执行的简便性,还可能因为错误的建模方式导致数据的冗余。仍然以前面的任务跟踪系统为例,倘若一个任务可以分配给多个员工,就会从一对多关系变为多对多关系。由于 Embedded 方式是将 Task 的数据直接嵌入到 Employee 中,如果别的 Employee 包含了相同的 Task就会导致 Task 数据的冗余。</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/领域驱动设计实践(完)/038 什么是模型.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/领域驱动设计实践(完)/040 数据设计模型.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":"70997e4daf143cfa","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>