DDD你真的理解清楚了吗?怎么准确理解“值对象”

news/2025/3/1 4:03:51/文章来源:https://www.cnblogs.com/mooodo/p/18634338

这些年,随着软件业的不断发展,软件系统开始变得越来越复杂而难于维护。这时,越来越多的开发团队开始选择实践DDD领域驱动设计。领域驱动设计是一种非常优秀的软件设计思想,它可以非常好地帮助我们梳理复杂业务,解决大规模业务系统的设计开发与更新维护。但是,领域驱动的学习成本却非常高,使得很多同学难于准确地理解DDD,更难于真正落地实际项目的设计编码。为此,我通过这一系列知识分享,让大家真正准确地理解DDD中这些晦涩的概念,特别是让大家理解最终是怎么落地到软件项目的设计开发中的。

今天,我们首先探讨的是在DDD中,让大家最头疼、最晦涩的概念——什么是“值对象”?要理解“值对象”的设计思想,首先我们先来梳理一下领域驱动的核心思想。领域驱动认为,软件的本质就是对真实世界的模拟,软件中所有业务逻辑正确与否,唯一的判定标准就是,是否与真实世界保持一致。如果一致,设计就是OK的,否则用户就会提BUG,或者变更需求。只要理解了这个本质,软件设计就简单了:我们首先理解真实世界的业务,然后将我们对业务的理解形成软件设计。

然而,这里有一个问题,那就是软件是怎么与真实世界对应的呢?这种对应体现在以下三个方面:

1)真实世界有什么事物,软件世界就有什么对象;

2)真实世界中这些事物有什么行为,软件世界这些对象就有什么方法;

3)真实世界中这些事物间有什么关系,软件世界中这些对象就有什么关联。

因此,我们在软件设计时,首先以业务场景为单位,一个一个地分析每个业务场景都有哪些领域对象,以及它们相互之间的行为与关系,形成领域模型。然后再以领域模型为核心,完成软件的设计与开发。

譬如说,我们要设计一套电子商务系统,按照业务需求会划分为很多功能,那么每个功能就是一个业务场景。譬如在“下单”这个业务场景中,在真实世界中都有哪些事物呢?首先有“订单”,可以形成订单对象;每个订单对应一个用户,但一个用户可以有多个订单,所以从订单到用户是一个“多对一”关系;一个用户有多个地址,然而一个订单只能对应一个地址……如下图,我们按照这样的步骤逐一分析领域对象和它们之间的关系。最后,我们对订单有什么操作呢?有“下单”、“支付”、“查看订单”……

这就是领域模型,订单、用户、地址、订单明细等类,都是领域对象。然而,在DDD中还要把领域对象严格区分为“实体”与“值对象”。这时,很多同学就比较晕,什么是实体?什么是值对象?为了准确理解这个地方,我们先看看《领域驱动设计》原著是怎么说的。

实体(Entity):又称为Reference Object,它具备生命周期,并且在生命周期的过程中,其形式和内容都有可能发生变化,但它的标识永远不变。也就是说,每个实体都有一个唯一的标识,用于区分真实世界中的“他”与其他人,比如用户ID。实体是有生命周期的,比如用户的注册与注销;实体中的一切都可能会变,比如小张将自己的账户转让给了李四,但账户ID是不会变的。这个唯一的标识就是主键,以这种形式的设计,用户有用户ID、账户有账户ID、订单有订单ID,就是实体的设计。很显然,这样的设计大家都能够理解,关键是“值对象”。

值对象(Value Object):在《领域驱动设计》这本书中对值对象的描述比较隐晦,但我们大致可以归纳为以下几个特征:对象声明与对象中的属性都是不变的,可以为多个对象所引用与共享(而不是复制),这就是“值对象”。该怎样来理解这几个特征呢?

譬如,在真实世界中,一个用户可以有多个订单,那么在订单查询时,每个订单就是一个订单对象,每个订单对象都有各自不同的订单ID。因此,订单的设计是实体。然而,每个订单都要引用一个用户,也就是指向一个用户对象。那么,同一个用户的多个订单指向的是谁呢?显然,不能将这个用户复制成多个对象,而是所有这几个订单都引用的是这一个对象。也就是说,这里的“用户”对象是值对象。

然而,值对象又必须是不变的,这就是说,如果“用户”是值对象,那么用户及其相关的属性都必须是不变的。但现实情况是,用户及其用户信息都是可变的,我们会对用户信息进行增删改操作。是的,在真实世界中,一切都是在变化,没有什么是不变的。那么,怎么来理解值对象的不变性呢?

在DDD的领域建模中,除了有领域模型以外,还有一个非常重要的设计叫“限界上下文”。领域模型是描述的真实世界,但真实世界又是无限大的,那么领域模型该如何描述真实世界呢?DDD将一个复杂系统的业务划分成很多个限界上下文,然后对每个限界上下文中进行领域建模。这样,每个领域模型都有各自的边界,我只是在这个边界中描述我的业务。这样的设计,就将纷繁复杂的世界“分而治之”,对每个领域模型的分析就变得简单了。

譬如在以上案例中,可以首先将对用户及其用户档案的管理划分成一个上下文,叫“用户上下文”。在这个上下文中,用户及其属性是可变的,要进行增删改的操作,因此“用户”对象的设计是实体。然而,在另一个“订单上下文”中,订单是实体,要对其进行增删改,但订单引用的“用户”以及“地址”就是值对象。也就是说,在订单上下文中,“用户”以及“地址”是只读的,仅仅用于订单对象的引用,而不会对其进行增删改操作。这样,“用户”以及“地址”作为值对象,就体现了它的只读与不变性。

此外,站在开发的角度来说,领域模型最终要落实到软件开发。如果采用微服务的设计,就会将“用户上下文”与“订单上下文”划分为用户微服务与订单微服务。在划分微服务的同时,也会划分数据库,用户微服务有用户数据库,订单微服务有订单数据库。有了这样的设计,用户表必然是设计在用户数据库中,而不是在订单数据库中。当订单微服务要查询订单时,通过调用用户微服务的接口获得订单相关的用户对象。然而,这些用户对象只存在于订单微服务的内存中,是只读的,而不会存储在订单数据库中去增删改。这就是“值对象”的概念及其设计实现。

总而言之,DDD的实体在本上下文中是可读可写的,而值对象是只读的。在一个上下文中的核心业务是对实体的增删改,而值对象是与实体相关联的,仅仅用于实体的引用与查询的对象。值对象的特性是只读,但在实际项目中的表现有2种形式:

1)  对其它微服务中对象的引用,需要调用其它微服务的接口获得,数据不在我本地的数据库中,仅仅存在于内存中,并且不能修改,只能查询;

2)  一些类似类型、种类、类别的字典数据,虽然数据存储在本地数据库中,但没有增删改的功能,只有查询与引用。例如会员等级、积分规则、支付方式,等等。

(待续)

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

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

相关文章

【AI+安全】sshd后门自动化检测 | BinaryAI在恶意软件检测场景的实践

原创 腾讯科恩实验室 腾讯科恩实验室 2024年11月12日 10:12 上海 一、引言 在网络安全攻防对抗中,攻击者经常通过在系统关键组件中植入后门程序,来获取持久的访问权限。sshd (SSH daemon) 作为管理远程登录的核心服务,是攻击者常用的目标之一。攻击者通过修改或者替sshd二进…

用Detr训练自定义数据

前面记录了Detr及其改进Deformable Detr。这一篇记录一下用Detr训练自己的数据集。先看下Detr附录中给出的大体源码,整体非常清晰。接下来记录大体实现过程 一、数据准备 借助labelme对数据进行标注然后将标注数据转换成COCO格式,得到以下几个文件其中JPEGImages存放所有图片…

8086汇编(16位汇编)学习笔记05.asm基础语法和串操作

https://bpsend.net/thread-121-1-2.htmlasm基础语法 1. 环境配置xp环境配置 1.拷贝masm615到指定目录 2.将masm615目录添加进环境变量 3.在cmd中输入ml,可以识别即配置成功dosbox环境配置 1.拷贝masm611到指定目录 2.将masm611所在目录添挂载进dosbox 3.将masm611目录在dosbo…

WinNTSetup 系统安装利器 v5.4.0 单文件版

软件介绍 WinNTSetup,系统安装利器,目前最好用的系统安装器,Windows系统安装部署工具。支持所有Windows平台,支持多系统安装、完全格式化C盘、支持创建VHD虚拟硬盘、在Windows及PE系统下运行,允许在安装前对系统进行预优化设置、集成驱动程序、启用第三方主题支持、加入无…

解决 Cannot GET /favicon.ico

一、报错 二、定位(项目所在文件夹) 三、改名(添加图片,重命名)

Java编程规范-DO / BO / DTO / VO / AO的使用

Java 开发 DO / BO / DTO / VO / AO 的作用 Java 开发中,DO(Data Object)、BO(Business Object)、DTO(Data Transfer Object)、VO(View Object) 和 AO(Application Object) 是常用的对象类型,每种类型都在特定的层次和场景中发挥不同的作用。以下是它们的定义和使用…

硬件开发笔记(三十二):TPS54331电源设计(五):原理图BOM表导出、元器件封装核对

前言一个12V转5V、3.3V和4V的电源电路设计好了,下一步导出BOM表,二次核对元器件型号封装,这是可以生产前的最后一步了。 导出BOM表步骤一:打开原理图打开项目,双击点开原理图:   步骤二:报告-元器件列表列宽一点,板子元器件种类规格不多的时候,导出的东西也不多,因…

数字孪生-智能制造

1、数字企业内循环:打造端到端的数字化应用体验 2、GARTNER分层架构 3、企业数字化架构 4、数字企业的两大核心特征 6、产品数字主线赋能企业转型 7、数字主线关键技术:基于统一架构构建产品全量数字模型 8、闭环数字化解决方案 9、基于数字主线的设计-仿真-试验协同 10、产品数…

C# WPF PrintDialog 打印(3)

前面https://www.cnblogs.com/yinyu5/p/18634080使用PrintDocument方法打印了Canvas,这里打印下面的DataGrid列表内容:这里DataGrid的数据源是DataTable,后台代码:1 private void PrintDocument_DataTable_Method(string Title, DataTable dataTable)2 {3 …

【JAVA代码审计】记一次某java类的cms最最最详细的代码审计

前言 刚好遇到一个授权的渗透是通过该cms实现getshell,所以顺便审计一下java类的cms,这个管理系统是一个内容管理系统,下载地址 https://gitee.com/oufu/ofcms/tree/V1.1.3/tomcat下载地址 https://dlcdn.apache.org/tomcat/tomcat-8/v8.5.78/bin/apache-tomcat-8.5.78-wind…