对DDD的常见误区

这里是Z哥的个人公众号

每周五11:45 按时送达

当然了,也会时不时加个餐~

我的第「205」篇原创敬上

大家好,我是Z哥。

我从 2014 年开始接触 DDD 到现在也有 7 年多时间了,在这个期间踩过很多坑,也是自己慢慢从充满疑问,然后一步一步摸索着到如今可以轻松落地 DDD 的地步。

在 2014 年那会,DDD 的社区远不如现在那么繁荣,甚至还有很多鄙视它的声音。因此网上的资料也很少,大部分的知识获取源自两本书《领域驱动设计 软件核心复杂性应对之道》和《实现领域驱动设计》。

所以,我的 DDD 之路和现在的很多人可能不太一样,我是沿着一个书本上浓缩后的核心思想,然后自下而上地通过自己的摸滚打爬慢慢构建的知识体系,而现在很多人是照着一些课程中教的偏实践型的知识,自上而下的模仿,然后再根据自己的理解和场景做出调整优化。

最近几年 DDD 一直很火,网上的课程也有很多。但是说实话,DDD 里面的大多数概念还是很晦涩的,没有那种特别清晰规则,可以像代码规范那样明确指导我们如何 coding,什么是好代码,什么是坏代码。

很多小伙伴从我之前在博客园写的 DDD 系列找过来,和我交流 DDD 相关的问题,发现大家对其中的不少概念还存在一些疑惑。或者说,有些概念自己觉得清楚了,但是落地的时候好像又不知道从何下手。

今天我们就来聊聊我收集到的一些大家的困惑,来和大家交流一下我的观点。

/01  DDD 必须要配合微服务一起用?/

这个问题还有另一种问法,“单体应用能用 DDD 吗?”。

先说结论,必须可以,不是微服务也可以用。

我觉得大家之所以形成这个误区背后的逻辑是这样的。因为 DDD 提倡构建领域模型,划定限界上下文,而限界上下文在微服务中恰好能体现为一个单独的Service,看上去是如此的天衣无缝,它们俩是一组黄金搭档,就该一起使用。

其实,在我看来这两者之间的关系并不是搭档关系,而是一种“道”和“术”的关系。DDD 是一种架构设计方法论,而微服务是一种具体的架构设计实践方案。前者是“道”,后者是“术”,符合某一个“道”的“术”从来都不只有一个。

可能你要问了,单体应用怎么用 DDD ?建议你可以尝试用模块的概念来划定限界上下文,并且将不同的模块分别独立一个包,以此达到类似 Service 的隔离效果。剩下的建模工作就没什么不同了。

/02  限界上下文只能通过拆分成独立的 Service 体现?/

这个问题其实在第一点里已经解答了,不用拆分成独立的 Service 也可以体现限界上下文。

如果你存在这个问题的困惑,大概率是你在平时的开发中没有带着「上下文」这个概念去 coding。

因为我们设计的每一个模型,其实都是对现实世界的抽象,而现实世界中的每一个概念都有它所对应的上下文,脱离上下文任何一个词都有歧义。哪怕是相同的一个词在不同的上下文里表达的含义可能不同,比如,售票系统的中的“座位”与设计电影院的规划系统里的“座位”并不是同一个东西。

/03  业务简单的项目不适合 DDD ?/

这个问题其实就类似于,面积小的房子做装修是不是不需要精心设计?只有那些高大上的别墅、甚至是大型场馆才需要精心设计?

我想答案是显而易见的。

不管是简单的项目还是复杂的项目,他们都不影响良好的抽象设计给项目带来的好处。

不过有些人可能纠结的点在于与 DDD 配套的技术框架、基础设施太复杂。我觉得这个想法就有点本末倒置了。不管是什么技术框架、组件,本身只是一种工具。你拿微服务的那套放到一个简单的单体应用里自然会觉得复杂。但是我们相比回到使用传统的三层架构,更应该是要找到,甚至是自己打造更合适的工具。

/04  Entity 的属性字段需要对应数据表吗?/

我认为这个问题的答案首先有一点是确定的,就是:不像三层架构那样,完全一一对应。

因为 Entity 里面可能会嵌套其它的 Entity,以及 ValueObject。所以里面的属性数量大多会比数据表的字段更多。

另外,我觉得这里最关键的问题是,Entity 的唯一标识该如何体现。

  • 是用一个 Guid ?还是直接业务标识?

  • 如果不用 Guid ,那么值对象如果需要持久化的话,该怎么办?

我自己在实践的时候的标准是,尽量只用业务标识,如果必须要用 Guid,确保它是不可修改,甚至是不可见的。因为 Guid 并不是领域知识的一部分,它只是为了解决技术层面问题而引入的一个东西。

/05  Application、DomainService 的区别?/

很多人不重视 DomainService 的设计,其实它非常重要。因为在领域知识中必然存在很多「行为」不属于任何一个 Entity。比如电商领域中的付款这个动作,到底是放在订单的 Entity 上?还是账户的 Entity 上?其实你会发现都不太适合。此时就是 DomainService 出场的时候了,它避免了某些 Entity 承担过多与其无关的业务,导致过于臃肿。

Application 的职责类似于一个调度器,用来调度各个业务。但是它不应该体现具体的细节,业务的细节应该被封装在 Entity、ValueObject 以及 DomainService 中。

有几个小技巧可以判断是否体现了业务。

  • 不要有 if / else 分支逻辑

  • 不要有任何针对 Entity、ValueObject 上属性的读取和修改操作

概括的来说,Application层只做一件事:将输入的CQE(Command、Query、Event),通过调用领域对象进行业务处理,然后将结果组装成 DTO 返回。

/06  防腐层在那一层做?Application层?/

我觉得防腐层应该放在 Infrastructure 层,与 IRepository 的实现 Repository 同层,为了便于区分,你也可以将它单独一个包。

防腐层中的各种 Facade 应该与 Repository 有共同的 IRepository 。因为防腐层背后具体要做的事情就是从其它系统获取数据,这个职责和 Repository 是一致的。Repository 屏蔽了操作数据库的细节,而防腐层屏蔽了对接其它系统的细节。

我们这次就聊这么多。以上 6 个问题都是有多个人同时提到过的问题,我想它们也更具普遍性。

最后我们总结一下。

  1. DDD 必须要配合微服务一起用?不是,微服务只是 DDD 思想的一种实现方式。

  2. 限界上下文只能通过拆分成独立的 Service 体现?不是,单体应用中通过「模块」也可以。

  3. 业务简单的项目不适合 DDD ?不是,DDD 这个方法论对任何项目都有帮助。

  4. Entity 的属性字段需要对应数据表吗?无法做到一一对应,因为 Entity 可能存在嵌套。

  5. Application、DomainService 的区别?Application不包含业务,仅对业务进行调度。DomainService 实现不属于任何一个 Entity 的方法。

  6. 防腐层在那一层做?Application 层?Infrastructure 层,与 IRepository 的实现 Repository 同层。

好了,希望对你有所帮助。

最后再讲个题外话,「六边形架构」和「洋葱架构」在这些年伴随着 DDD 的概念被普及。其实所谓的六边形架构,他们的边就是由 DDD 概念中的防腐层构建的。所谓的洋葱架构,就是在六边形架构的基础上,针对内部业务对象进行建模,而 DDD 就是一种建模思想。

推荐阅读:

  • 如何摆脱「自我否定」状态

  • 聊聊Go的三色标记法

原创不易,如果你觉得这篇文章还不错,就「点赞」或者「在看」一下吧,鼓励我的创作 :)

也可以分享我的公众号名片给有需要的朋友们。

如果你有关于软件架构、分布式系统、产品、运营的困惑

可以试试点击「阅读原文

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

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

相关文章

Python办公自动化Day2-openpyxl

目录 文章声明⭐⭐⭐让我们开始今天的学习吧!常规操作添加数据遍历所有单元格数据合并/取消合并单元格添加/删除行与列移动指定范围单元格 文章声明⭐⭐⭐ 该文章为我(有编程语言基础,非编程小白)的 Python办公自动化自学笔记知识…

vsftpd服务

实验1VSFTPD实验环境在虚拟机Linux 6.5系统下需要2台Linux系统一台A作为服务端一条B作为测试客户端开启2台Linux系统。实验目标A作为服务端配置VSFTPD服务器实现FTP服务。B作为测试客户端验证服务器A的共享是否有效。配置真实主机确保真实主机能ping通2台虚拟机。实验步骤1. 首…

传递函数_使用python计算麦克风阵列信号的传递函数

使用python写了一个测试麦克风阵列传递函数的demo,有需要的自取。该代码使用了第三方库ThinkDSP。1. 传递函数首先解释下什么是传递函数:把具有线性特性的对象的输入与输出间的关系,用一个函数(输出波形的拉普拉斯变换与输入波形的拉普拉斯变…

LNK2005 连接错误解决办法

nafxcwd.lib(afxmem.obj) : error LNK2005: "void * __cdecl operator new(unsigned int)" (??2YAPAXIZ) 已经在 LIBCMTD.lib(new.obj) 中定义nafxcwd.lib(afxmem.obj) : error LNK2005: "void __cdecl operator delete(void *)" (??3YAXPAXZ) 已经在 …

中国数学竞赛史上最玩命的“赌徒”,为了国家荣誉,他不惜用生命换来了五次世界第一...

全世界只有3.14 % 的人关注了爆炸吧知识知识君今天,要跟大家介绍的是,北大历史上首位被授予博士学位的人,深受癌症折磨的同时还带领着中国学子连续5次站上世界顶端的人,他的故事,他的品格,值得被所有人记住…

33条C#、.Net经典面试题目及答案[zt]

本文集中了多条常见的C#、.Net经典面试题目例如“.NET中类和结构的区别”、“ASP.NET页面之间传递值的几种方式?”,并简明扼要的给出了答案,希望能对学习C#、.Net的读者有所帮助。 1, 请你说说.NET中类和结构的区别? 答:结构和类…

控制用户的访问之权限、角色【weber出品必属精品】

权限的作用 限制用户对数据的访问 权限的分类 1. 系统权限:能够存取数据库的权限 2. 对象权限:操作数据库对象的内容 系统权限 1.1 如何创建用户: SQL> create user test default tablespace users identified by test;用户已创建。 1…

javq接口_Java为什么要使用接口_java接口怎么使用

Java接口是什么Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。接口(英语:Interface)&am…

[(转)hystar整理]Entity Framework 教程

预备知识 2 LINQ技术 2 LINQ技术的基础 - C#3.0 2 自动属性 2 隐式类型 2 对象初始化器与集合初始化器 3 匿名类 3 扩展方法 4 Lambda表达式 4 .NET中的数据访问 4 DataSet方案 5 改进的的DataSet方案 5 手写代码通过ADO.NET2…

Exchange中的数据库文件

Exchange中的数据库文件Exchange中的数据库文件是edb文件、stm文件和众多的log文件组成.其中以edb结尾的文件是数据库文件.以stm结尾的是流数据文件,以log结尾的是事物日志文件.<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />E…

13岁上中科大,17岁攻读哈佛博士,“天才”尹希的开挂人生

全世界只有3.14 % 的人关注了爆炸吧知识2013年美国斯隆基金会(Alfred P. Sloan Foundation)颁发的美国斯隆研究奖获得者尹希&#xff0c;31岁哈佛最年轻的华人教授&#xff0c;获2017年“豪华版诺贝尔奖”之称的科学突破奖-物理学新视野奖&#xff0c;这年唯一的获奖华裔。知识…

强制升级?!.NET Core 2.1容器镜像将从Docker Hub中删除

前言.NET Core 2.1将于2021年8月21日结束支持&#xff0c;本来应该没什么影响&#xff0c;该怎么用继续用得了。但是&#xff0c;如果你在生产环境使用了.NET Core 2.1容器镜像&#xff0c;那就要注意了&#xff0c;从8月21日开始&#xff0c;.NET Core 2.1容器镜像将不再在Doc…

浅谈云计算核心技术

云计算一个高大上的名字&#xff0c;现在我也准备开始学习了&#xff0c;今天看了一本云计算的普及性书籍&#xff0c;将云计算的一些核心技术做一个简要的概括吧。虚拟化技术云计算跟虚拟化不能等同&#xff0c;有位专家说过&#xff0c;虚拟化提供了很好的底层技术平台&#…

“史上“最疯狂”的顶级数学家,看完后忍不住感慨太神了”

▲ 点击查看说起世界上最顶级的数学家&#xff0c;你的脑海中会浮现的&#xff0c;大概是欧拉、高斯、香农等数学巨匠。但是要说起成就和神秘性兼备的&#xff0c;一定就是——称为印度之子的——拉马努金。1887年12月22日&#xff0c;拉马努金出生在印度一个穷困潦倒的家庭&am…

JavaScript 游动层onmouseover

<html> <head> <meta http-equiv"Content-Type" content"text/html; charsetGB2312"> <title>学生信息系统</title> <script language"javascript"> var showInfo new function () { this.showLayer func…

.NET 6 新特性 —— Random.Shared

.NET 6 新特性 —— Random.SharedIntro最近微软发了一篇 .NET 6 性能提升的博客文章&#xff0c;里面提到了很多有趣的东西&#xff0c;其中有一个是 Random.Shared这是一个只读的静态属性&#xff0c;并且是一个线程安全的对象&#xff0c;这个东西可以帮助我们简化 Random 对…

《Python CookBook2》 第一章 文本 - 检查字符串中是否包含某字符集合中的字符 简化字符串的translate方法的使用...

检查字符串中是否包含某字符集合中的字符 任务&#xff1a; 检查字符串中是否出现了某个字符集合中的字符 解决方案&#xff1a; 方案一&#xff1a; import itertoolsdef containAny(seq,aset):for item in itertools.ifilter(aset.__contains__,seq):return Truereturn False…

mysql高级查询 二_MySQL高级查询(二)

EXISTS 和NOT EXISTS子查询EXISTS子查询语法:SELECT ……… FROM 表名 WHERE EXISTS (子查询);例:SELECT studentNo AS 学号,studentResult 成绩 FROM resultWHERE EXISTS(/*查询LOgic Java最后一次考试成绩大于80的记录*/SELECT * FROM result WHERE subjectNo(SELECT subject…

What's blocking my lock? 转载

原文地址 &#xff1a;http://www.orafaq.com/node/854 Create a blocking lock To begin, create a situation where one user isactively blocking another. Open two sessions. Issue the following commands inSession 1 to build the test table: SQL> create table ts…

致家长:疫情期间教育好自己的孩子,就是你最重要的事业!

全世界只有3.14 % 的人关注了爆炸吧知识疫情危机&#xff0c;却也是教育的契机。现在孩子呆在家里&#xff0c;老师管不了孩子&#xff0c;却也正是家长重新认识父母之责的契机。家长才是孩子的第一任老师&#xff0c;家长的言传身教、一举一动都影响着孩子成长。孩子是你的唯一…