DDD学习笔记四
领域模型的构建
-
基础领域模型的基本组成有名称、属性、关联、职责、事件和异常
-
发掘领域概念3种策略:
1)学习已有系统,重用已有模型
2)使用分类标签。分类标签来源于领域,需要我们研究一些资料并做一些提炼。从采用5W法开始,即事件(What)、原因(Why)、地点(Where)、时间(When)、人员(Who),之后在其上加以补充
3)识别名词短语。
一般情况下,我们会从策略1开始,组织中完全没有之前相关的旧系统的情况并不多见,之后策略2和3结合使用 -
概念类命名的注意事项如下:
使用领域术语。记住,模型的首要任务是沟通,使用领域专家熟悉和习惯的名字,即使你认为有更精确的表达方式。
对于相同的名称但不同场景的情况,应该将最易于识别的名字留给最常用的类型,比如“订单”可以在支付场景和配送场景中使用,
应该 把它用在支付环节而不是配送环节,因为支付环节更加的核心,更符合这个名字的本身的含义,业务逻辑也更复杂。
尽量避免使用编程语言中的保留字,比如“集合”。
不要凭空创造概念或添加前缀和后缀来区分名称,这只会增加沟通的障碍。始终以领域专家和系统用户喜欢的方式进行沟通,
这是DDD的内在特殊要求 -
创建关联
阅读导向箭头“►”是可选的,它指示关联名称阅读的方向,并不表示可见性或导航的方向
关联表示为类之间的连线,关联的末端可以包含多重性
采用“主谓宾”结构为关联命名,连接线上只显示谓语
关联符号包含“0”时,不管是“”“0…”,还是“0…1”都客观上意味着这是一种可选关联,并不一定发生
关联类允许将关联本身作为类,并且使用属性、操作和其他特性对其进行建模。
-
使用关联类标准
1)通用语言中就有关联类的概念,如×××合同,用来约束两个模型之间的关系。
2)模型的某些属性与关联相关,这些属性脱离关联将显得无意义。
3)关联类的实例具有依赖于关联的生命周期,如合同期、项目时长等。
4)两个模型之间有多对多关联,并且存在与关联本身相关的信息。
其中第3条特征明显,可作为一个常用的标准。 -
关联简化
1)限制关联数量
2)限制关联方向
3)使用限定关联 -
定义属性
当领域模型需要记住信息时,引入属性,
属性的3种类:基本类型、通用的值对象和项目中的其他业务对象(前两种类型一般写在类图框中,而第三种使用关联来表达)
-
使用方法:
该操作的访问速度较慢。为了避免线程阻塞,甚至需要考虑为该操作提供一个异步版本,那么将其建模为属性就可能导致开销过大。
特别是那些访问网络或文件系统的操作,更应该使用方法而非属性。
该操作是一个转换操作,如toString()方法。
该操作每次返回的值不同,即使传入的参数不变。比如,Guid().NewGuid()方法每次都返回不同的值。
该操作有严重的、能观察到的副作用,比如抛出异常。
该操作返回一个集合。返回集合的属性容易误导用户,比如,Employees和Addresses等属性就是不合适的。
需要指定参数才能访问的属性,如前面多个地址的例子。
除以上情况外,请尽量使用属性。 -
使用属性建模时,还有一些细节值得注意:
1)如果不想让调用方改变属性的值,要创建只读属性。
2)不要提供只写属性,也不要让设置方法的存取范围比获取方法更广。如果一个属性值没有获取的意义,比如配置资源地址,此时应该提供set方法而不是属性。
3)要为所有属性提供默认值。技术上默认值不会导致异常或安全漏洞,业务上也往往体现一定的“默认”的领域含义
4)要允许用户以任意顺序来设定属性的值
5)如果属性设置方法抛出异常,则要保留属性原来的值。这在业务上也是合理的。
6)不要在属性获取方法中抛出任何异常,这会令人感到困惑。如果确实存在这种可能性,请改用方法 -
分配职责
业务职责则由其方法来定义
-模型层但职责:分配策略-信息专家模式
在信息专家模式中,职责分配给了具有完成该职责所需信息的模型。信息包括模型的各种成员,如字段、属性、关联,以及对资源的可见性等。
如果一个对象能够承担相应的职责,那么最好不要让更大的、具有更多信息的对象来承担这些职责
领域逻辑不要与技术架构混为一谈,领域逻辑要与任何持久化机制解耦
-
创建领域服务完成职责
如果一个职责需要的信息无法由一个模型提供,应该怎么处理?可以分为以下两种情况:
1)增加关联以获取必要的信息。
2)使用领域服务。
当一个职责发现采用信息专家模式没有找到合适的模型时,需考虑创建一个领域服务模型是否更好 -
方法是职责和操作的实现,方法参数设计的注意事项如下
1)应使用类层次中最接近基类的类型来作为方法参数,并保证该类型能够提供成员所需信息。这样做可以使得方法的适用范围更广,因为子类能取代任何基类的位置,即子类可以传入以基类定义的参数,但反过来则不行
2)不要将指针、指针数组和多维数组作为方法的参数。即使不在DDD团队中,且代码要保证优秀可读性的场合,指针和多维数组也是难以应对的。可以对方法进行重新设计以避免使用这些类型作为参数。
3)尽量不使用输出参数,如out、ref关键字修饰的参数。这会增加代码的复杂性,给领域模型的验证和测试带来干扰。同理,尽量使用值对象而不是实体类型的参数,因为值对象没有状态,传入的是一个副本。
4)可以使用重载(Overload)机制来简化方法的命名。重载即模型拥有很多同名的方法,但它们的参数不同,根据传入的参数匹配对应的方法。不要增加无谓的名称,以减少沟通的难度。使用重载机制让方法的命名更加顺畅。 -
触发事件
-
领域事件是一个特殊的模型成员。领域模型状态的改变(属性)或者执行操作时达到某些判定条件,都可能触发事件
(1)事件的捕获
领域事件是领域专家所关心的发生在领域中的有影响力的事件。将领域中发生的活动建模为一系列离散事件,每个事件都用领域对象来表示,领域事件是领域模型的组成部分,用于表示领域中所发生的事。
定义有两个层面:一是纯业务层面,领域事件来源于领域专家的通用语言,具有业务含义;二是设计层面,领域事件可以建模为一个领域对象,但这种对象是一种不可变类,也就是值对象,它将成为其他领域对象的一部分
事件的命名是过去时态,事件的载体为类图,其中包含的属性称为事件参数,它们是该事件消费者需要的关于事件的信息
-
领域事件的3个本质:
操作因果关系的体现。
为未来处理逻辑留下的逻辑占位符。
提供操作的异步性。 -
领域事件的作用。
保证模型间的数据一致性。
替换批处理操作。
系统集成。
-
处理异常
异常是一种特殊的意外情况处理机制
异常处理是领域模型要考虑的一部分,原因在于模型的责任不可能无限大。在遇到自己处理能力之外的情况时,要采用异常机制报告错误,并将处理权转交。异常就是这样一种机制,某种程度上,它可以保证领域模型的纯洁性,让其只关注于核心逻辑,而不用包含一堆意外情况处理代码 -
自定义异常的注意点:
要避免太深的继承层次,一般Exception类即可满足要求。
一定要以Exception作为后缀。
要使异常可序列化。为了使异常能够跨应用程序和跨远程边界工作,这样做是必须的。
要提供以下常用的构造函数。
要把与安全性有关的信息保存在私有的异常中,确保只有可信赖的代码才能得到该信息。比如数据库连接抛出的各类异常,可能会泄露你的表命名、表结构等信息。
可以为异常定义属性,这样就能从程序中取得与异常有关的额外信息。 -
抛出异常的注意点如下:
在领域模型中,要使用异常来处理意外情况而不是错误码。
不要在能处理的正常流程中抛出异常。
要为所有的自定义异常构建一份文档,使开发人员能够掌握,让他们能使用最合理、最具针对性的异常,比如不要使用“集合超容”来描述“购物车已满”。
在异常消息中避免使用感叹号和问号。
注意异常消息的本地化。
除了异常本身的类图,也可以在相关领域模型中列出可能抛出的异常
典型的领域逻辑建模
-
领域逻辑主要包括规则与约束、流程与分支、验证与筛选、算法与计算、时间与空间、有状态与无状态、同步与异步等
在模型中,使用大括号{}来标记规则
-
规则与约束
当规则和判断条件众多时,把这个约束提取到一个单独的方法中,并用具有业务含义的名称来对其命名
约束既可以放在模型内部,又可以单独将其提炼出来作为策略类或领域服务
需要单独提炼的验证规则的特点:
验证约束所需的信息并不属于这个对象。
相关规则在多个对象中出现,造成了代码重复,违反了“一处一个事实”原则。
很多领域逻辑是围绕这些约束展开的,它的层级不适合下放到实体或值对象的方法中。将约束单独作为一个模型,可以避免重要的领域概念被淹没,使通用语言更加顺畅 -
流程与分支
业务流程建模对应的是UML中的活动图
活动图对应的是协作图或时序图而不可能是类图,它们展示了一个用例的参与对象,以及对象之间传递的消息
,协作图中的节点已经变成了各个领域模型,其中的逻辑分支用中括号[]来标记,如图5-32中的[支付成功]。在业务流程图的基础上映射绘制出来的协作图,不仅能交流业务,还能直接指导实现开发,是对流程与分支建模的最理想模型。
-
验证与筛选
规格模式是将一组判断条件封装成一个单独的值对象,名称中一般带有形容词,表达了很强的领域含义,代表客户对于程度的看法,如“大额存单”“过期发票”“优秀成绩”等。如图5-34所示,这个值对象会有一个是否满足的判断方法(一般是IsSatisfiedBy()函数),用于校验某个对象是否满足该规格所表达的条件,只返回真假校验。多个规格对象还可以灵活组装,生成新的规格对象。
在业务逻辑上,规格与约束的不同点在于,约束是必须满足的,而规格只是从集合中筛选或验证符合条件的成员
规格的用法主要有以下3种:
1)验证对象,检查它是否满足某些条件。
2)筛选符合条件的对象。
3)创建满足某种条件的新对象 -
算法与计算
算法逻辑涉及领域的核心逻辑,其本身往往是内聚的,将其独立出来是最值得倡导的建模实践,如复杂的候选人排名规则、销售佣金的计算策略、保单的风险评估等。算法逻辑独立出来后,避免了复杂算法对领域模型的干扰,领域中的其他模型就可以专注于表达“做什么”的逻辑,而把“如何做”的复杂细节转移给了算法模型
领域中的算法可以建模为算法对象,常见于实时系统、科学和工程领域中。如果算法需要的信息都在实体或值对象内部,可以将该算法建模为该对象的操作。但如果该算法内聚且明显独立于领域对象而变化,那就不适合绑定一个领域对象,要显式地将其建模为算法对象,而不是隐藏在模型和代码深处。算法对象声明关键字是<>,标记在类图或协作图上以表明身份
算法对象经常会封装计算其算法所需要的数据,这些数据可以是初始化数据、中间结果或阈值数据。算法对象的主要职责是执行算法,不要把任何业务协调职责放入其中
将算法封装为领域服务是最佳选项,尤其是涉及多个实体类型、返回值为值对象的计算时。
将领域计算逻辑单独建模而不放在其他业务对象内的优点如下:
1)不给领域模型增加负担。如果计算所需的信息超出了该对象的范围,又会增加不必要的耦合。
2)关于计算逻辑的领域概念变得更加清晰,单独的计算规则一般都是核心域范畴,单独出来可以增加通用语言的词汇。
建模为操作符重载
操作符重载就是让领域模型可以像基本类型那样进行加(+)、减(-)、乘(*)、除(/)操作,还包括等于(==)、大于(>)、小于(<)等比较操作
当一个类内部出现public static的方法时,应该考虑该方法是继续放在这个类中还是单独成领域服务 -
时间与空间
定时约束建模通常遵循以下策略:
1)对于交互中的每条消息,考虑其开始时间和触发周期。将这个实时特征建模为消息的定时约束。
2)对于交互中每个值得关注的消息序列,考虑是否有一个相关的最大的相对时间。将这个实时特征加为对该消息的定时约束。
把系统对时间的要求表示为约束{},并放在对应的消息旁边即可。时间是动态逻辑,只能展示在时序图或协作图上。
计时器对象是由外部计时器激活的控制对象。计时器对象要么自己执行某个动作,要么激活另一个对象来执行期望的动作
计时器属于应用服务而不是领域服务
-
时效建模 : 时效是指与时间段相关联的信息
建模的方式是维护与一组时间间隔相关的信息,而不是单个值
-
建模空间指模型在分布式系统中的位置
-
有状态与无状态
建模为独立模型的状态:
领域模型具有多个状态,它们会根据一定条件进行转换。
不同的状态会导致不同的行为。
未来还可能会增加新的状态。
最佳方法是对这些状态建模,而不只是用条件语句加以判断 -
同步与异步
相关文章:

DDD学习笔记四
领域模型的构建 基础领域模型的基本组成有名称、属性、关联、职责、事件和异常 发掘领域概念3种策略: 1)学习已有系统,重用已有模型 2)使用分类标签。分类标签来源于领域,需要我们研究一些资料并做一些提炼。从采用5W…...
Head First设计模式中的典型设计模式解析与案例分析
Head First设计模式中的典型设计模式解析与案例分析 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 《Head First设计模式》是一本广受欢迎的书籍,…...

iptables 防火墙(一)
iptables 防火墙(一) 一、Linux 防火墙基础防火墙分类 二、iptables 的表、链结构规则表规则链数据包过滤的匹配流程 三、编写防火墙规则iptables 的安装iptables的基本语法规则的匹配条件通用匹配隐含匹配显式匹配 四、总结 在网络安全的世界里…...

数据库物理结构设计-定义数据库模式结构(概念模式、用户外模式、内模式)、定义数据库、物理结构设计策略
一、引言 如何基于具体的DBMS产品,为数据库逻辑结构设计的结果,即关系数据库模式,制定适合应用要求的物理结构 1、在设计数据库物理结构前,数据库设计人员首先 要充分了解所用的DBMS产品的功能、性能和特点,包括提供…...

QT加载安装外围依赖库的翻译文件后翻译失败的现象分析:依赖库以饿汉式的形式暴露单例接口导致该现象的产生
1、前提说明 VS2019 QtClassLibaryDll是动态库,QtWidgetsApplication4是应用程序。 首先明确:动态库以饿汉式的形式进行单例接口暴露; 然后,应用程序加载动态库的翻译文件并进行全局安装; // ...QTranslator* trans = new QTranslator();//qDebug() << trans->…...

13_旷视轻量化网络--ShuffleNet V2
回顾一下ShuffleNetV1:08_旷视轻量化网络--ShuffleNet V1-CSDN博客 1.1 简介 ShuffleNet V2是在2018年由旷视科技的研究团队提出的一种深度学习模型,主要用于图像分类和目标检测等计算机视觉任务。它是ShuffleNet V1的后续版本,重点在于提供更高效的模…...

Linux系统编程--进程间通信
目录 1. 介绍 1.1 进程间通信的目的 1.2 进程间通信的分类 2. 管道 2.1 什么是管道 2.2 匿名管道 2.2.1 接口 2.2.2 步骤--以父子进程通信为例 2.2.3 站在文件描述符角度-深度理解 2.2.4 管道代码 2.2.5 读写特征 2.2.6 管道特征 2.3 命名管道 2.3.1 接口 2.3.2…...

docker-本地部署-后端
前置条件 后端文件 这边是一个简单项目的后端文件目录 docker服务 镜像文件打包 #命令行 docker build -t author/chatgpt-ai-app:1.0 -f ./Dockerfile .红框是docker所在文件夹 author:docker用户名chatgpt-ai-app:打包的镜像文件名字:1.0 &#…...
TLS + OpenSSL + Engine + PKCS#11 + softhsm2 安全通信
引擎库路径只有在 /lib 下才能被 "LOAD" 识别到,OpenSSL的ReadMe给的示例在/lib,大概是在构建OpenSSL时默认的configure指定了lib路径 // #define PKCS11_ENGINE_PATH "/usr/lib/x86_64-linux-gnu/engines-1.1/pkcs11.so" #define …...

Unity实现简单的MVC架构
文章目录 前言MVC基本概念示例流程图效果预览后话 前言 在Unity中,MVC(Model-View-Controller)框架是一种架构模式,用于分离游戏的逻辑、数据和用户界面。MVC模式可以帮助开发者更好地管理代码结构,提高代码的可维护性…...

【简单讲解下OneFlow深度学习框架】
🎥博主:程序员不想YY啊 💫CSDN优质创作者,CSDN实力新星,CSDN博客专家 🤗点赞🎈收藏⭐再看💫养成习惯 ✨希望本文对您有所裨益,如有不足之处,欢迎在评论区提出…...

FastGPT 调用Qwen 测试Hello world
Ubuntu 安装Qwen/FastGPT_fastgpt message: core.chat.chat api is error or u-CSDN博客 参考上面文档 安装FastGPT后 登录, 点击右上角的 新建 点击 这里,配置AI使用本地 ollama跑的qwen模型 问题:树上有3只鸟,开了一枪&#…...

Golang-GMP
GMP调度 golang-GMP语雀笔记整理 GMP调度设计目的,为何设计GMP?GMP的底层实现几个核心数据结构GMP调度流程 设计目的,为何设计GMP? 无论是多进程、多线程目的都是为了并发提高cpu的利用率,但多进程、多线程都存在局限性。比如多进程通过时…...

【PythonWeb开发】Flask自定义模板路径和静态资源路径
在大型的 Flask 项目中,确实可能会有多个子应用(Blueprints),每个子应用可能都有自己的静态文件和模板。为了更好地管理和组织这些资源,可以使用static_folder 和template_folder 属性来统一管理。必须同时设置好主应用…...

Java对象创建过程
在日常开发中,我们常常需要创建对象,那么通过new关键字创建对象的执行中涉及到哪些流程呢?本文主要围绕这个问题来展开。 类的加载 创建对象时我们常常使用new关键字。如下 ObjectA o new ObjectA();对虚拟机来讲首先需要判断ObjectA类的…...

Does a vector database maintain pre-vector chunked data for RAG systems?
题意:一个向量数据库是否为RAG系统维护预向量化分块数据? 问题背景: I believe that when using an LLM with a Retrieval-Augmented Generation (RAG) approach, the results retrieved from a vector search must ultimately be presented…...
Rust-11-错误处理
Rust 将错误分为两大类:可恢复的(recoverable)和 不可恢复的(unrecoverable)错误。对于一个可恢复的错误,比如文件未找到的错误,我们很可能只想向用户报告问题并重试操作。不可恢复的错误总是 b…...
自动化测试:使用Postman进行接口测试与脚本编写
Postman 是一种流行的 API 测试工具,广泛应用于开发和测试过程中。它不仅可以用于手动测试,还支持自动化测试和脚本编写,以确保 API 的可靠性和性能。本文将详细介绍如何使用 Postman 进行接口测试与脚本编写,帮助你更高效地进行自…...

ONLYOFFICE 8.1 桌面编辑器测评:引领数字化办公新潮流
目录 前言 下载安装 新功能概述 1.PDF 编辑器的改进 2. 演示文稿中的幻灯片版式 3.语言支持的改进 4. 隐藏“连接到云”板块 5. 页面颜色设置和配色方案 界面设计:简洁大方,操作便捷 性能评测:稳定流畅,高效运行 办公环…...

基于大语言模型LangChain框架:知识库问答系统实践
ChatGPT 所取得的巨大成功,使得越来越多的开发者希望利用 OpenAI 提供的 API 或私有化模型开发基于大语言模型的应用程序。然而,即使大语言模型的调用相对简单,仍需要完成大量的定制开发工作,包括 API 集成、交互逻辑、数据存储等…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...
DiscuzX3.5发帖json api
参考文章:PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下,适配我自己的需求 有一个站点存在多个采集站,我想通过主站拿标题,采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...

算术操作符与类型转换:从基础到精通
目录 前言:从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符:、-、*、/、% 赋值操作符:和复合赋值 单⽬操作符:、--、、- 前言:从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...