DDD领域驱动设计初探
DDD 强调领域模型要兼顾业务和技术两个视角。
我们怎么用一套系统化的方法,抽丝剥茧、一步一步地把需求落实到代码呢?咱们看看下面这张图,它表示了领域驱动设计中的主要流程。
领域驱动设计主要的开发流程你可以看到,在整个开发流程中,首先是要捕获行为需求,也就是传统软件工程里的**“获取需求”**。这一步,我们要识别需求里有哪些流程、哪些功能,每个功能由什么人操作,会产生什么结果。
在传统软件工程中,这一步常用的方式是用例建模,也就是用 Use Case 来建模。在这套课程里面,我们会用 DDD 中比较流行的一种方法,叫做**“事件风暴”**。
接下来,我们就可以进行领域建模了,也就是通过建立领域模型,把需求里的主要业务知识描述清楚。DDD 的领域模型,大体上相当于传统软件工程中的分析模型。
基于领域模型,我们就可以做架构设计,包括进程间和进程内的架构。比如说微服务设计、中台设计都属于进程间架构。而 DDD 分层架构,通常说的是进程内架构。
然后就可以根据领域模型进行数据库设计,最后是代码实现。
这样,就形成了一个基于 DDD 的开发闭环。其实,在实践中,尤其是对敏捷软件开发来说,这些步骤不是线性的,而是反复迭代、互相穿插的。DDD 是以领域模型为核心的。所以,我们可以把上面说的步骤分成模型的建立和模型的实现两部分。模型的建立阶段,使用的都是业务术语,归根结底来自业务人员,业务人员不仅能听懂,而且负责评价建模的正确性。而模型的实现,则是业务人员不需要理解也不关注的,会包含技术实现方面的内容。这两者的边界很重要,我们在后面还会反复提到。总结
事件风暴
这里的第一步是识别领域事件,在这一步,我们要找到业务流程中发生了哪些事情;
第二步是识别命令,进一步说明是什么角色,做了什么操作,导致上述事情的发生;
而第三步是识别领域名词,从领域事件和命令中找到名词性概念,为进一步的领域建模打下基础。这里每一步的具体做法和要点,我们后面都会详细介绍。
事件风暴的第一步:识别领域事件
所谓领域事件,就是在业务过程中,业务人员要关注的那些已经发生的事儿。比方说,对于电子商务系统,订单提交、商品签收等等,都是领域事件。实际上,领域事件表示的是,业务流程中每个步骤引发的结果。事件风暴的作者认为,从结果入手来梳理需求,比从操作入手,更容易把业务想清楚。事件风暴中的“事件”两个字就来源于领域事件。
从结果梳理
识别出领域事件和业务规则。
订单创建,订单下发(订单审单以后才能下发)
关于领域事件,我们还要注意下面这两点。
第一,不要把技术事件当成领域事件。领域事件一定要是领域专家所关注的,用的是业务术语。像数据库事务已回滚、缓存已命中之类的技术术语,不是领域事件,不在这个阶段讨论。
第二,查询功能不算领域事件,这部分内容详见CQRS。领域事件应该是对某样事物产生了影响,并被记录的事情。一般是某个事物的创建、修改和删除。还有一种情况是向其他人或者系统发消息,例如“通知邮件已发送”也算领域事件,因为接收方可能会通过进一步处理来影响某些事物。
事件风暴第二步:识别命令
所谓命令(command),就是引发领域事件的操作,我们可以通过分析领域事件得到。除了识别出命令本身以外,我们通常还要识别出谁执行的命令,以及为了执行命令我们要查询出什么数据。
谁,做了什么动作,造成了什么结果(增删改)
客户小二 人工审单 审单完成后订单下发
事件风暴第三步:识别领域名词
操作人 通过 某个命令 对领域名词 做了什么动作造成了什么领域事件
客户小二 人工审单 订单 订单下发
领域名词 订单
事件风暴是业务人员和技术人员一起协作,捕获行为需求、消化领域知识、形成统一语言的一种方法。这是就整体来说的。
必须没有歧义,比如技术说新建用户,业务说新增客户。对于业务来说,客户的物理实体是天然存在的没有创建一说,在这里更为合理。
画模型图
识别出领域实体以后,画uml类图,理清一对一一对多关系,限制,泛化等诸多关系就可以完成领域建模了。
这里建模是一个很考验技能的操作,因为长期以来,开发人员都会被一个叫做sql优化的事情搞的很头疼,而如果建模技能很纯熟,画出的模型就可以单表代替多表联查,这样不仅sql执行效率很高,而且加索引和分库分表也会很方便。
这个过程完善约束,整理出业务词汇表
拆分模块
根据模型图里的交互,可以把相关性比较高的一组模型组合成一个模块。
数据库建表
DDD 主张要根据领域模型来进行数据库设计,保证数据库和领域模型的一致,从而保证数据库和业务需求以及代码的一致性。在进行数据库设计时,我们可以用物理数据模型图,也可以直接用建表语句,两者基本是等价的。为了直观,我们采用了图示的方法。对数据表、字段等等的命名,应该依据词汇表,以便保证统一语言。一般来说,领域模型中的实体映射为数据库中的表;领域模型中的属性,映射成表中的字段。同时还要根据需求补充更多的字段。模型中的一个一对多关联,可以映射成一个外键字段,以及一个外键约束。但基于云的应用一般不会真的建立外键约束,而外键的逻辑关系还是存在的。我们用虚线箭头表示这种逻辑上的外键关系,称为虚拟外键。对于多对多关联,我们必须增加一个关联表,其中包括了两个实体表各自的主键。另外,关联上的多重性决定了外键字段的非空约束。
也可能多个领域模型对应的是同一个表。
代码分层设计
变化的依赖稳定的,外部的依赖内部的。
domain 领域模型,用来封装领域数据和逻辑。这一层与领域模型直接对应,是整个系统的核心;
实际实现会有一些妥协,比如应用间传递的DTO放在这里,会把本来是业务层面的东西VO,DO也丢进来。
不做妥协就是写Builder,领域层自己用基本类型和领域层的基本对象构建出领域对象。灵活使用看各位的取舍。
application根据领域模型组合的业务编排,并且处理事务、日志等横切关注点
适配器 主动适配器例如Controller适配http请求,xxxService适配rpc请求,还有防腐层,做模型DTO2DO转换的
被动适配器如Repository 适配操作领域模型的数据库物理表。
这样就可以把领域模型与数据库解耦
Repository → dao→物理表
公共层
例如一些底层支持,日志,用于存放工具和框架。这一层对前面的各层进行支撑。
领域逻辑的识别过程
如果一个逻辑需要和领域专家讨论才能确认的,就是领域逻辑;如果领域专家根本不感兴趣的,多半就是应用逻辑。
界限上下文
一个应用或者模块内部,用聚合根交互。所谓聚合根就是指比如一个订单,包含多个子单,只暴露主单对外交互,子单通过对应主单才能获取,保证数据在编码的隔离。
工作中落地
第一种是新建系统。也就是说现在刚好有一个新项目,可能是要开发一个全新的系统,也可能是为现有系统新增或重写一个比较大的模块。这时候,领导希望保证质量,降低风险,觉得需要方法学的支持,因此要引入 DDD。对于比较大的项目,前期的总体规划和设计还是必要的。但要意识到,这时候产生的模型还是方向性的、粗粒度的,之后开发过程中要随着对问题理解的深入,不断演进。
第二种是改造现有系统。常见的情况是,某个对公司很有价值的系统,已经维护了很多年,系统架构和质量日益腐化,很难维护,不能满足快速变化的业务需求。这时候希望引入 DDD 对系统进行比较大的重构,使系统重新“焕发青春”。另一种情况是企业要进行服务化改造,把一些现有的单体应用拆分成微服务。
第一步是反推领域模型。新建系统的时候是从需求到模型,可以叫做正推。而由于现有系统已经存在了,所以我们做的第一步,反而是从系统现状中“反推”出当前的领域模型,目的是客观地反映出系统当前的领域知识和逻辑。这时候的模型往往有不少问题,比如不能正确反映领域知识、存在矛盾、冗余等。
第二步是建立目标领域模型。根据当前系统的痛点、问题以及业务需求,就可以建立目标领域模型,作为改进的方向。建立目标领域模型,一定要有明确的“时间点”。
第三步是设计演进路线。有了当前模型和目标模型,就可以分析两者之间的差距。跨越这个差距的过程就是改进的过程。设计演进路线最大的问题就是怎么保证可行性。一般要把改进过程化整为零,迭代实施,并且还要兼顾日常的业务需。
第四步是迭代实施。最好基于敏捷软件开发方法,小步快跑地实施。在这个过程中,必然会对之前建立的目标领域模型进行反馈,不断改进。同时还要不断评估开发现状,保证不偏离目标。
在一个相对独立的模块往往称为“精益切片”。精益切片的难度、范围、风险要适中,最好在 3 个月内形成最小闭环。
DDD 的知识点还是比较多的,而且其中有一些理解起来有一定难度。如果在推广过程中,一下子就让所有人掌握所有知识点,往往会造成很多误解,导致动作走形,影响推广效果。所以,一开始可以聚焦在 DDD 最核心的问题上,暂时省略其他要点,推行一个“低配版”的 DDD。等到大家掌握了基本技能,需要更深层次的运用时,再引入其他知识点。那么在开始的时候,哪些可以省略,哪些不能省略呢?我梳理了一张表,供你参考。
衡量团队DDD水平
其他补充
其实对于DDD来说,是否使用充血模型,只是形式上的区别,关键在于一定要有识别出领域对象,和业务专家统一语言,这样就能无障碍沟通,然后慢慢把这个领域模型推广到整个系统,推广过程调用到外部服务,就会遇到适配层,过了适配层另一个应用就可以用到界限上下文,聚合根。这个过程中对项目代码重新分层,完成业务系统的改造,用更抽象的方法,解耦项目,拥抱变化的需求,沉淀统一的服务能力。
主要讲了一些概念性的,落地可以看cola框架
CQRS
就是读服务与其他会产生数据变动的命令区分开。可以专门维护一个用于读的表,或者构筑一级二级缓存实现。
相关文章:
DDD领域驱动设计初探
DDD 强调领域模型要兼顾业务和技术两个视角。 我们怎么用一套系统化的方法,抽丝剥茧、一步一步地把需求落实到代码呢?咱们看看下面这张图,它表示了领域驱动设计中的主要流程。 领域驱动设计主要的开发流程你可以看到,在整个开发流…...
C中AES_cbc_encrypt加密对应java中的解密
前言知识: 1.AES(Advanced Encryption Standard)高级加密标准,作为分组密码(把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文)。 2.在AES标准…...
演化算法:乌鸦搜索算法 (Crow Search Algorithm)
前言 如果你对这篇文章感兴趣,可以点击「【访客必读 - 指引页】一文囊括主页内所有高质量博客」,查看完整博客分类与对应链接。 在机器学习中,我们所要优化的问题很多时候难以求导,因此通常会采用一些演化算法(又称零…...
基于open62541的OPC UA服务器和客户端开发技术
一、OPC UA的基本概念 1、OPC(OLE for Process Control),是一个工业标准,管理这个标准的国际组织是OPC基金会; 2、OPC通信结构:是指包含一个或多个OPC客户端与服务器相互通信的集合。以下是一个简单的流程图:标准的C/S结构。 3、OPC服务器:TOPC基金会定义了四种;...
测试测开面试要知道的那些事01
列表与元组的区别列表是动态数组,它们可变且可以重设长度(改变其内部元素的个数)。元组是静态数组,它们不可变,且其内部数据一旦创建便无法改变。元组缓存于Python运行时环境,这意味着我们每次使用元组时无…...
物联网毕设 -- 智能厨房监测系统(改)
前言 在家庭生活中,厨房是必不可少的,所以厨房的安全问题关乎着我们大家的生命,所以提出智能厨房监测系统,目的就是为我们减少不必要的安全问题 ⚠️⚠️(本文章仅提供思路和实现方法,并不包含代码&#x…...
macOS 13.3 Beta 3 (22E5236f)发布
系统介绍3 月 8 日消息,苹果今日向 Mac 电脑用户推送了 macOS 13.3 开发者预览版 Beta 3 更新(内部版本号:22E5236f),本次更新距离上次发布隔了 7 天。macOS Ventura 带来了台前调度、连续互通相机、FaceTime 通话接力…...
Failed to configure a DataSource: ‘url‘ attribute
一 完整的错误信息 *************************** APPLICATION FAILED TO START *************************** Description: Failed to configure a DataSource: url attribute is not specified and no embedded datasource could be configured. Reason: Failed to dete…...
Mysql高级——锁
锁 mysql锁的分类 从性能上分为:乐观锁、悲观锁从锁的粒度上分:行锁、间隙锁、页锁、悲观锁从对数据库的操作分类:读锁、写锁 乐观锁需要我们自己通过version字段来实现,如果更新失败则在代码中进行where重试。而我们常见的读锁…...
Spring的Async注解线程池扩展方案
目录- [Spring的Async注解线程池扩展方案]- [目录]- [1. 扩展目的]- [2. 扩展实现]- [2.1 扩展Async注解的执行拦截器AnnotationAsyncExecutionInterceptor]- [2.2 扩展Async注解的Spring代理顾问AsyncAnnotationAdvisor]- [2.3 扩展Async注解的 Spring Bean 后置处理器AsyncAn…...
wfb-ng 锁定WiFi接口
wfb-ng 锁定WiFi接口1. 源由2. 需求3. 分析4. 步骤4.1 确认网卡MAC地址4.2 修改udev配置文件4.3 配置重载&重启4.4 确认逻辑网卡接口4.6 修改wfb-ng逻辑WiFi通信接口5. 参考资料6. 补充资料为了更加方便的调试和使用wfb-ng软件,解决由于设备枚举发现时命名可能存…...
Python所有方向的入门和进阶路线,20年老师傅告诉你方法
干了20多年程序员,对于Python研究一直没停过,这几天把我自己对Python的认知和经验,再结合很多招聘网站上的技术要求,整理出了Python所有方向的学习路线图,基本上各个方向应该学什么,都在上面了,…...
RLOAM/RO-LOAM
LOAM框架 LOAM框架包含三个步骤: Scan registration:从原始激光扫描点数据中提取点特征。点特征是角点或者面点。 odometry estimation:在特征提取之后,特征点传递到里程计模块,通过特征匹配和优化步骤计算相对坐标变…...
JUC并发编程之Semaphore-应用与深度源码剖析
目录 JUC并发编程之Semaphore-应用与深度源码剖析 1. Semaphore 是什么? 2.怎么使用Semaphore? 2.1构造方法 2.2 重要方法 2.3 基本使用 需求场景 基础版代码实现 tryAcquire()引入代码实现 acquireUninterruptibly(),acquire()对比代码实现 3.…...
JWT详细介绍使用
一、JWT介绍 JWT是JSON Web Token的缩写,即JSON Web令牌,是一种自包含令牌。 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。 JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务…...
C/C++开发,无可避免的多线程(篇六).线程池封装类
一、线程池概念 线程池是一种多线程处理方式,它包含一个线程工作队列和一个任务队列。当有任务需要处理时,线程池会从线程工作队列中取出一个空闲线程来处理任务,如果线程工作队列中没有空闲线程,则任务会被放入任务队列中等待处理…...
HIVE中如何实现针对IPv6 CIDR的查询
Hive默认情况下不支持IPv6 CIDR查询,因为IPv6 CIDR查询需要使用一些额外的函数。 但是可以通过使用UDF(用户自定义函数)来实现这一点。 IPv6 CIDR表示为网络地址/前缀长度,其中网络地址是一个IPv6地址,前缀长度是一个介于0和128之间的整数,表示网络地址中前多少位是网络…...
【微信小程序】-- 生命周期(二十八)
💌 所属专栏:【微信小程序开发教程】 😀 作 者:我是夜阑的狗🐶 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询! &…...
Kafka 概述
Kafka 概述Broker消费者Kafka 属于分布式的消息引擎系统,主要功能 :提供一套完备的消息发布与订阅解决方案 生产者和消费者都是客户端(Clients): 生产者(Producer):向主题发布消息…...
详解Java8中如何通过方法引用获取属性名/::的使用
在我们开发过程中常常有一个需求,就是要知道实体类中Getter方法对应的属性名称(Field Name),例如实体类属性到数据库字段的映射,我们常常是硬编码指定 属性名,这种硬编码有两个缺点。 1、编码效率低&#x…...
0106广度优先搜索和最短路径-无向图-数据结构和算法(Java)
1 单点最短路径 单点最短路径。 给定一幅图和一个起点s,回答“从s到给定目的顶点v是否存在一条路径?如果有,找出其中最短的那条(所含边数最少)。“等类似问题。 深度优先搜索在这个问题上没有什么作为,因为…...
僵尸(Zombie)进程
文章目录1.僵尸进程2.产生僵尸进程的原因3.利用 wait 函数销毁僵尸进程4.使用 waitpid 函数销毁僵尸进程1.僵尸进程 进程完成工作后(执行完 main 函数中的程序后)应被销毁,但有时这些进程将变成僵尸进程,占用系统中的重要资源。这…...
JS实现:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?
题目:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 数列是 1,1,2,3,5,8,13,21....观察可以看出来从第三个数字开始…...
Verilog如何编写一个基础的Testbench
本文将讲述如何使用Verilog 编写一个基础的测试脚本(testbench)。在考虑一些关键概念之前,先来看看testbench的架构是什么样的。架构包括建模时间、initial块(initial block)和任务(task)。此文…...
基于JavaEE社区物业管理系统开发与实现(附源码资料)
文章目录1. 适用人群2. 你将收获3.项目简介4.技术栈5.测试账号6.部分功能模块展示6.1.管理员6.2.业主1. 适用人群 本课程主要是针对计算机专业相关正在做毕业设计或者是需要实战项目的Java开发学习者。 2. 你将收获 提供:项目源码、项目文档、数据库脚本、软件工…...
问一下ChatGPT:DIKW金字塔模型
经常看到这张DIKW金字塔模型图,还看到感觉有点过份解读的图,后面又加上了insight,impact等内容。 Data:是数据,零散的、无规则的呈现到人们眼前,如果你只看到这些数字,如果没有强大的知识背景&a…...
javaScript基础面试题 ---闭包
闭包1、闭包是什么?2、闭包可以解决什么问题?3、闭包的缺点1、闭包是什么? 闭包是一个函数加上到创建这个函数的作用域的链接,就是一个作用域可以访问到另一个作用域的变量,闭包‘关闭’了函数的自由变量 function f…...
如何自定义您的网站实时聊天图标
实时聊天图标是您网站上的一个按钮,可在访问者单击时打开实时聊天。它代表了您的企业与客户沟通的门户。这是您的网站访问者与您联系、提出问题和接收个性化推荐的一种方式,聊天图标的设计最好是简单且引人入胜,个性化的图标往往更能提现企业…...
Vue侦听器Watch
31. Vue侦听器Watch 1. 定义 Watch是Vue.js提供的一个观察者模式,用于监听数据的变化并执行相应的回调函数。虽然计算属性Computed在大多数情况下更合适,但有时也需要一个自定义的侦听器Watch。因为在有些情况下,我们需要在状态变化时执行一…...
云快充研发中心平台架构师谈云原生稳定性建设之路
作者:吕周洋 大家好,我是来自云快充研发中心的平台架构师吕周洋,今天我给大家分享云快充云原生稳定性之路。 点击查看:云快充研发中心平台架构师 吕周洋:云快充云原生稳定性治理之路 云快充成立于2016年,…...
西安单位网站制作/网络推广的优势有哪些
文章目录1背景2传感器2.1光电编码1背景 由于移动机器人车体的非线体轮胎与地面的轻微滑动和非完整约束等原因。无法建立一个精确数学模型.使许多基于模型的移动机器人运动方案存在困难 移动机器人的运动控制。精确地进行自定位是一个基本的要求/自定位就是获得机器人相对于一个…...
网站报价模板/打开百度
为什么80%的码农都做不了架构师?>>> 过滤选择器主要是通过特定的过滤规则来筛选出所需的DOM元素,过滤规则与CSS中的伪类选择器语法相同,及选择器都以一个冒号(:)开头。按照不同的过滤规则,过滤选择器可以分为基本过滤…...
单页面网站好优化吗/网站做seo教程
---- 什么是CAS机制 CAS机制主要是发生于Java中原子操作类(JUC)的底层实现中,其中在CAS机制中包含3个基本参数:内存地址V、旧预期值A、要修改的新值B。 当要更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实…...
怎么做微网站/免费制作网页平台
一个类型允许定义多个实例构造器,在使用过程中确实是十分方便的。但是,在定义这些构造器时,如果稍不留神,可能就使你的代码编译后产生了好多不必要的垃圾,增加了程序集的大小,也不够简洁。 例如:…...
服装网站建设目标/科学新概念seo外链
软考中涉及扩展与包括关系的辨认,先不说考试的事,扩展与包括关系是UML中非经常见、非常基础的一种关系,然而我发现非常多同学都特别easy混淆,甚至软考真题中都存在题目和答案有待商榷的地方。此篇博客实属楼主匆忙而著,文章简明、短小&#x…...
泉州手机网站建设公司哪家好/seo常用工具
本文实例讲述了php封装的图片(缩略图)处理类。分享给大家供大家参考,具体如下://图片处理工具类class Image{//属性private $thumb_width; //缩略图的宽private $thumb_height;//错误属性public $thumb_error;//构造方法public function __construct($wi…...