【DDD】学习笔记-数据模型与对象模型

在建立数据设计模型时,我们需要注意表设计与类设计之间的差别,这事实上是数据模型与对象模型之间的差别。

数据模型与对象模型

我们首先来分析在设计时对冗余的考虑。前面在讲解数据分析模型时就提及,在确定数据项模型时,需要遵循数据库理论的设计范式,其中一个目的是避免数据冗余。但是,避免了数据冗余并不意味着代码能支持重用。例如,员工表与客户表都定义了“电子邮件”这个属性列。该属性列在业务含义上是完全相同的,但在数据表设计时,却只能分属于两个表不同的列,因为对于数据表而言,“电子邮件”列其实是原子的,属于 varchar 类型。

如果针对业务概念建立对象模型,需要遵循“高内聚低耦合”的设计原则,如果发现多个属性具有较强的相关性,需要将其整合起来共同定义一个类。例如国家、城市、街道和邮政编码等属性,它们都与地址相关,共同组成完整的地址概念,在对象模型中就可以定义 Address 类。

在数据模型中,关系数据表并不支持自定义类型,在设计时又需要支持一范式(1NF),即确保数据表的每一列保持原子性,就必须将这个内聚的组合概念进行拆分。例如,地址就不能作为一个整体被定义为数据表的一个列,因为系统需要访问地址中的城市信息,如果仅设计为一个地址列,就违背了一范式。这时,地址在数据模型中就成了一个分散的概念。若要保证其概念完整性,唯一的解决方案是将地址定义为一个独立的数据表;但这又会增加数据模型的复杂性,更会因为引入不必要的表关联而影响数据库的访问性能。正如 Jimmy Nilsson 所说:“关系模型是用来处理表格类型的基本数据的,这既有好的一面,也有坏的一面。面向对象模型很善于处理复杂数据。”

针对同样的业务概念,我们可以对比数据模型与对象模型之间的差异。例如,员工、客户与地址的数据模型如下图所示:

enter image description here

虽然员工与客户都定义了诸如 country、city 等地址信息,但它们是分散的,并被定义为数据表提供的基本类型,无法实现两个表对地址概念的重用。对象模型就完全不同了,它可以引入细粒度的类型定义来体现丰富的领域概念,封装归属于自己的业务逻辑,同时还提供了恰如其分的重用粒度:

41397947.png

对比这两个模型,组成数据模型的数据表是一个扁平的数据结构,数据表中的每一列都是数据库的基本类型,而组成领域模型的类则具有嵌套的层次结构。在设计时,更倾向于建立细粒度对象来表达一个高度内聚的概念,如 Address 与 ZipCode 类。

在建立数据设计模型时,与数据表对应的持久化对象往往难以表达业务的约束规则。例如,运输(Shipping)与运输地址(ShippingAddress)满足“每个 Shipping 必须**有且只有一个 **ShippingAddress”这一业务规则。在数据模型中,可以通过在运输与运输地址之间创建关系来表达,例如在可视化的 ER 图中,用虚线代表任选,用实线代表强制。但这种关系连线虽然表达了这种约束关系,却没法显式地体现这一业务概念,除非在数据模型图中采用注解来说明。如果采用对象模型,就可以通过引入 ShippingSpecification 这个类型来体现这种约束逻辑。

从设计模型看,构成数据模型主体的数据库与数据表,明显存在粒度和边界的局限性。这种局限性在一定程度上影响了数据建模的质量。关系数据库的设计范式并没有从类型复用的角度去规定数据表的设计,由于关系表不支持自定义类型,无法支持 Jimmy Nilsson 所说的“复杂数据”,因此可以认为在数据模型中,数据表才是最小的复用单元。由于建立一个数据表存在 I/O 成本,会影响数据库的访问性能,因而在数据模型中,通常不建议为细粒度但又是高内聚的数据类型单独建立数据表,如前面给出的“地址”的例子。换言之,关系数据库的设计范式仅仅从数据冗余角度给予了设计约束,如果照搬数据模型去建立类模型,就有可能无法避免代码冗余。

对于一个数据库而言,关系数据库的表结构是扁平的,数据表之间可以建立关联,也可以隐式地通过一对多的关系表达具有层级的父子关系,但数据模型自身却无法体现这种层次。下图是 Apache OFBiz 项目中关于运输相关的数据模型:

53705545.png

这个数据模型一共定义了 31 张数据表,这些表对应的业务概念上存在主从关系,以及强弱不同的耦合关系。例如,Shipment 表显然是主表,诸如 ShipmentAttribute、ShipmentStatus、ShipmentType 与 ShipmentItem 等都是围绕着 Shipment 表建立的从表。但是,数据模型自身却无法体现这种主从关系。我们之所以能识别出这种主从关系,其实是基于对数据表名的语义推断。通过语义推断,我们也能判断 Shipment 与 ShipmentItem 等表之间的关系要明显强于 Shipment 与 PicklistBin、Picklist、PicklistRole 等表之间的关系,但数据模型并没有清晰地表达这种边界。

究其原因,在关系数据库的数据模型中,数据库是最大的复用单元。设计数据库时,往往是一个库对应一个子系统或者一个微服务,而在数据库和数据表之间,缺少合适粒度的概念去维护数据实体的边界。它缺少领域驱动设计引入的聚合(Aggregate)、模块(Module)等各种粒度的边界概念。显然,扁平的关系型数据结构无法体现领域概念中丰富的概念层次。

NoSQL 的数据设计模型

NoSQL 数据库的设计模型就截然不同了,尤其是文档型的 NoSQL 数据库,能够通过定义嵌套关系的无模式数据表相当自然地体现对象图(Object Graph)的结构。因此,在针对 NoSQL 数据库建立数据设计模型时,就可以直接运用领域建模的设计原则,如引入聚合的概念来设计表模型。

Martin Fowler 在文章 Aggregate Oriented Database 中指出,NoSQL 数据库需要有效地将数据存储在分布式集群之上,而他则建议存储的基本数据单元应为领域驱动设计中的聚合(Aggregate),聚合的粒度天然地满足了诸如数据分片这样的分布式策略。Martin Fowler 以订单为例,说明了关系数据库与 NoSQL 数据库的不同,如下图所示:

55277936.png

一个订单对象在关系数据库中需要被分解为多张数据表,但对于诸如 MongoDB、Elasticsearch 这样的数据库,则可以认为是一个聚合。因此,在设计 NoSQL 的数据模型时,可以运用领域驱动设计中聚合的设计原则。

我在设计一个报表系统的报表元数据管理功能时,选择了 Elasticsearch 作为存储元数据的数据库。在设计元数据管理的数据模型时,就通过聚合来思考元数据中 ReportCategory、Report 与 QueryCondition 三者之间的关系。

从业务完整性看,Report 虽属于 ReportCategory,但二者之间并没有强烈的约束关系,即不存在业务上的不变量(Invariant)。ReportCategory 可以没有 Report,成为一个空的分类;我们也可以撇开 ReportCategory,单独查询所有的 Report。倘若我们将 Report 放到 ReportCategory 聚合中,由于 Report 可能会被单独调用,聚合的边界保护反而成为了障碍,这样的设计并不合理。因此,ReportCategory 和 Report 应该属于两个不同的聚合。

分析 QueryCondition 与 Report 之间的关系,又有不同。当 QueryCondition 缺少 Report 对象后,还有存在意义吗?答案一目了然,没有 Report,就没有 QueryCondition。皮之不存毛将焉附!因此可以确定 Report 与 QueryCondition 应属于同一个聚合。于是,我们得到如下模型:

73579695.png

这样设计获得的模型显然是一个领域模型。当我们将其以 JSON 的格式持久化到 Elasticsearch 的数据表时,又可以认为该模型同时就是 Elasticsearch 的数据模型。

这种面向文档的嵌套层次结构与对象模型更为相配,并在多数时候采用 JSON 结构来表达数据结构。JSON 数据结构在许多产品和项目中得到运用,一些传统的关系型数据库也开始向这个方向靠拢。例如,目前流行的开源关系数据库如 MySQL 和 PostgreSQL,都已支持 JSON 这样的文档型数据结构。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/669966.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Sentinel(理论版)

Sentinel 1.什么是Sentinel Sentinel 是一个开源的流量控制组件,它主要用于在分布式系统中实现稳定性与可靠性,如流量控制、熔断降级、系统负载保护等功能。简单来说,Sentinel 就像是一个交通警察,它可以根据系统的实时流量&…

算法学习——LeetCode力扣链表篇1

算法学习——LeetCode力扣链表篇1 203. 移除链表元素 203. 移除链表元素 - 力扣(LeetCode) 描述 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val val 的节点,并返回 新的头节点 。 示例 示例 …

一个查看armv8系统寄存器-值-含义的方式

找到解压后的SysReg_xml_v86A-2019-12目录 wget https://developer.arm.com/-/media/developer/products/architecture/armv8-a-architecture/2019-12/SysReg_xml_v86A-2019-12.tar.gz wget https://developer.arm.com/-/media/developer/products/architecture/armv8-a-archi…

ChatGPT辅助编程,一次有益的尝试

如果大家想学习PCIe,搜索网上的信息,大概率会看到chinaaet上Felix的PCIe扫盲系列的博文 Felix-PCIe扫盲 每次看这个系列博文的时候,我都在想有没有什么方法可以把这个系列的博文都保存到一个pdf文件中,这样方便阅读。于是有了下…

蓝桥杯省赛无忧 课件99 裴蜀定理

前置算法 欧几里得算法 01 什么是裴蜀定理 02 裴蜀定理的数学证明 03 裴蜀定理扩展 04 例题 关联知识 EXGCD(扩展欧几里得算法)

SSRF漏洞给云服务元数据带来的安全威胁

文章目录 前言元数据服务威胁1.1 Metadata元数据1.2 RAM资源管理角色1.3 STS 临时凭据利用1.4 CF云环境利用框架1.5 元数据安全性增强 TerraformGoat2.1 永久性AccessKey2.2 SSRF靶场环境搭建2.3 腾讯云CVM配角色2.4 接管腾讯云控制台 SSRF组合拳案例3.1 上传图片功能SSRF3.2 文…

ubuntu22.04@laptop OpenCV Get Started: 001_reading_displaying_write_image

ubuntu22.04laptop OpenCV Get Started: 001_reading_displaying_write_image 1. 源由2. Read/Display/Write应用Demo2.1 C应用Demo2.2 Python应用Demo 3. 过程分析3.1 导入OpenCV库3.2 读取图像文件3.3 显示图像3.4 保存图像文件 4. 总结5. 参考资料 1. 源由 读、写、显示图像…

Android中设置Toast.setGravity()了后没有效果

当设置 toast.setGravity()后,弹窗依旧从原来的位置弹出,不按设置方向弹出 类似以下代码: var toast Toast.makeText(this, R.string.ture_toast, Toast.LENGTH_SHORT)toast.setGravity(Gravity.TOP, 0, 0)//设置toast的弹出方向为屏幕顶部…

蓝桥杯刷题day07——斐波那契与7

1、题目描述 斐波那契数列的递推公式为:FnFn-1Fn-2, 其中F1F21. 请问, 斐波那契数列的第 1 至 202202011200 项(含)中, 有多少项的个位 是 7 。 答案提交 这是一道结果填空的题, 你只需要算出结果后提交即可。本题的结果为一 个整数, 在提交答案时只填…

云计算运营模式介绍

目录 一、云计算运营模式概述 1.1 概述 二、云计算服务角色 2.1 角色划分 2.1.1 云服务提供商 2.1.2 云服务消费者 2.1.3 云服务代理商 2.1.4 云计算审计员 2.1.5 云服务承运商 三、云计算责任模型 3.1 云计算服务模式与责任关系图 3.2 云计算服务模式与责任关系解析…

刚刚晋升为管理者,还不会如何管理团队?你要重点关注这9个策略

管理团队需要明确团队目标、提前要求承诺、明确组织架构、团队高效协作、洞察员工、引入敏捷、执行可视化、及时反馈和复盘优化。 这样管理团队可以极大提高团队组织能力。团队组织能力强大的话,团队成员是可以实现自我管理的,会自我驱动去完成目标和执…

第01课:自动驾驶概述

文章目录 1、无人驾驶行业概述什么是无人驾驶智慧出行大趋势无人驾驶能解决什么问题行业趋势无人驾驶的发展历程探索阶段(2004年以前)发展阶段(2004年-2016年)成熟阶段(2016年以后) 2、无人驾驶技术路径无人…

华为OD机试真题C卷-篇3

文章目录 查找一个有向网络的头节点和尾节点幼儿园篮球游戏 查找一个有向网络的头节点和尾节点 在一个有向图中,有向边用两个整数表示,第一个整数表示起始节点,第二个整数表示终止节点;图中只有一个头节点,一个或者多…

【从0上手Cornerstone3D】如何使用CornerstoneTools中的工具之同步器

同步器(Synchronizers)可以使多个视图同步响应同一个工具的操作,例如我们在MPR视图下,同步操作三个视图的缩放程度、windowLevel等等 一个同步器必须需要以下几个部分才可以执行 一个监听事件(什么情况下触发同步&…

88.网游逆向分析与插件开发-物品使用-物品使用策略管理UI的设计

内容参考于:易道云信息技术研究院VIP课 上一个内容:物品交换的逆向分析与C封装-CSDN博客 码云地址(ui显示角色数据 分支):https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号:f1b9b1a69ac3e2c3…

私有化部署一个吃豆人小游戏

目录 效果 安装步骤 1.安装并启动httpd 2.下载代码 3.启动httpd 使用 效果 安装步骤 1.安装并启动httpd yum -y install httpd 2.下载代码 进入目录 cd /var/www/html/ 下载 git clone https://gitee.com/WangZhe168_admin/pacman-canvas.git 3.启动httpd syste…

【Qt Design】界面介绍

文章目录 前言Widget Box(工具箱)对象查看器Qt Design属性编译器sizePolicy内容 信号/槽编辑器资源浏览器ui文件 前言 Widget Box(工具箱) 提供很多控件 对象查看器 对象查看区域,可以查看主窗口放置对象的列表 …

鸿蒙内核框架

1 内核概述 内核简介 用户最常见到并与之交互的操作系统界面,其实只是操作系统最外面的一层。操作系统最重要的任务,包括管理硬件设备,分配系统资源等,我们称之为操作系统内在最重要的核心功能。而实现这些核心功能的操作系统模…

教授LLM思考和行动:ReAct提示词工程

ReAct:论文主页 原文链接:Teaching LLMs to Think and Act: ReAct Prompt Engineering 在人类从事一项需要多个步骤的任务时,而步骤和步骤之间,或者说动作和动作之间,往往会有一个推理过程。让LLM把内心独白说出来&am…

LLM(大语言模型)——大模型简介

目录 概述 发展历程 大语言模型的概念 LLM的应用和影响 大模型的能力、特点 大模型的能力 涌现能力(energent abilities) 作为基座模型支持多元应用的能力 支持对话作为统一入口的能力 大模型的特点 常见大模型 闭源LLM(未公开源…