你或许以为你不需要领域驱动设计

640?wx_fmt=jpeg

作者:邹溪源,长沙资深互联网从业者,架构师社区合伙人!



犹记得刚刚参加工作时,是地图厂商四维图新集团旗下的一家子公司,主要从事规划测绘相关软件研发的公司。当时我的项目是为勘测设计院提供相对应的应用软件,对地理信息和规划相关的图纸信息领域的认知,几乎已经专业水平。事实上,规划设计大概和软件设计类似,有规划的设计、或无规划的设计,造成的结果几乎是天壤之别。


我们或许很容易就能设想到一个毫无规划设计的城市,纵横交错的路网、杂乱无章式的建筑布局、各种凌乱的棚户区设计,恰好象征着软件设计的无序性,也恰好体现了软件企业在经费不足、组织缺乏管理、开发者能力不足、软件随时随地想改就改时的行业现状,只能说这样的软件是最能符合当时实际劳动生产力水平的产品。


640?wx_fmt=jpeg

图一:巴西棚户区
如图一所示,巴西棚户区,层层叠叠、风格迥异、密密麻麻,如果作为一个外人贸然来到这样的地方,大概很容易迷失期间、更不用说充斥在棚户区的各类毒品和黑社会。杂乱无章的建筑和街区,就像代码中错综复杂的调用链;而借助贫民区搞事的黑社会就像是代码中的异味或者bug,表面上看起来如此平静、与世无争、但是你永远也不知道啥时候会来一冷枪。


不要以为离我们很远,我们其实轻易就能写出这样的软件工程项目。不一定是“大泥球”系统,也有可能只是一些看似简单的业务系统,但内部代码逻辑,可能会复杂到令人窒息的程度。也许那个时候有个别开发者也许会试图靠自己的能力来改变局面,但是往往也会碍于屎山太大,难以下咽。


大概只有最顶级的规划设计师、耗费足够多的资源,才能将这样的软件系统进行整改。然而,即便如此,如果以后没有持续维护的手段、更好的设计、仅靠老程序员或个别架构师、盲目相信将单体服务拆分成微服务,几乎不太可能实现软件未来的可持续发展。


一个良好的软件产品的一生、或许其实是一家企业一生的真实写照。


在特定组织架构下,缺乏技术基因的组织有时候期待技术变革,却会开启新的泥坑。而那些渴望靠技术改变一切的技术专家,虽然拥有某些大厂微服务式架构、以及架构改造的经验,他们也试图通过自己的努力,为企业业务腾飞助力。而在他们过去的经验中,往往相信组织遇到的问题,用微服务一定能解决问题。然后大肆扩招,一年内从几个人的规模、扩招到数百人的规模,将原来的系统从单体服务、改良成为微服务。但是靠单枪匹马根本无力拯救大势,没有更好的业务拆分策略,就只能按照数据库的表名关系实现了最简单的拆分。架构改造并非每次都会百试百灵,有时甚至连原来的需求都包不住,毕竟只能看到用户界面层外观上的表面逻辑,而隐藏在业务中的那数十万行代码,哪怕包含了企业最有价值的经验财富,也由于代码过于混乱,最终抛弃在源代码管理器中,堪称化神奇为腐朽。

老系统改造也好、新系统开发也好,毫无疑问,我们最容易相信的其实是老程序员经验,而程序员们掌控系统的方式,就是靠数据库建模来驱动软件开发的古老模式,而且几乎都是面向过程式的代码,这些代码的流程几乎一模一样,只需简单的按照步骤,一步步套模式,轻易就能学会。


1、查看用户界面,定义需要绑定到界面的模型和层级结构。


2、设计数据库,不管什么类型的项目,先根据客户提供的业务表单、将其转化成实体关系(ER图)、然后建立对应的代码模型。有可能使用专业软件设计ER图,也有可能会使用Navicat软件设计ER图。


3、设计接口,然后把数据拼凑成用户界面层所需的对象。


4、代码层次结构为传统的三层架构,严格按照用户界面层、业务逻辑层、数据访问层进行设计,有时候会引入依赖注入框架,实现不同层次间的解耦。


但是有时候程序员不会严格区分需要编写的代码,究竟是属于哪个层次应该囊括的内容。于是毫无疑问,如果代码是为了实现用户界面上某些数据绑定操作,代码就往用户界面层写;或者代码是为了实现从数据库中抽取某些复杂数据、并构造成满足用户表现层逻辑的查询对象,那么就可以看到数据访问层代码中那些臃肿的SQL语句或查询方法。


正如“罗马不是一天建成的”,屎山也同样如此。这样的写法在代码刚刚编写之初并没有问题,只是随着业务变化、时间的积累、程序员的水平、方法重构、新技术新组件的引入,代码将成为屎山。


这时,高级程序员们的价值,就在于他如何能够在屎山中快速找到bug、并解决问题的能力,这大概是一种不能复用、不可再生的能力,因为永远有让人看不懂的垃圾代码,而且每家企业都有自己的特点,不同企业间往往不能循环利用。我一位朋友经常吐槽,他感觉自己的价值就是守住公司那份拥有8年历史的古老代码,以便其他程序员在进行代码修改时,不会引发莫名其妙的bug让系统无法运转。



在现代软件工程学的教科书中,都会指出面向对象是解决软件复杂性的方法,但实际上掌握这种方法的开发者并不多。由于开发者普遍缺乏抽象化思维,所以面向数据库、面向过程式的编程习惯能够成为业界主流,并非时代的倒退,而仅仅只是在短期效率和长期维护性上,被迫做出的艰难选择。


假设我们设计出的符合三层架构的系统结构图简化后,如下图所示:

640?wx_fmt=png


我们来看看这种数据库建模的开发流程中的输出成果:


1、会定义两种对象,分别是是面向UI层的模型(DTO)和数据实体(Entity)。在领域驱动设计中,将这两种称为所谓贫血模型,贫血模型,只有赋值器Set和取值器Get,(在Java里面会使用POJO 这个名词来定义)。贫血模型是为了作为保存状态或传递对象而存在,他并非按照实际用例场景对某类具体事务的抽象、也没有与对象相关的行为。


2、定义数据访问层来实现数据的持久化、或者从持久层实现数据的创建过程。数据访问层存在的目的是为了构建上述贫血模型对象,这种访问机制被成为“事务脚本”。事务脚本与对象行为割裂,而且容易导致异味产生。


3、与用户行为相关的操作割裂的存放在不同层。有的可能放在用户界面层、有的可能放在数据访问层、有的可能放在业务逻辑层,造成了领域知识的丢失。


4、用户界面层使用接口作为外观或者一种行为、开发者会使用自己独立的风格习惯来定义这种行为,就容易造成术语和规则不统一,也会为后期产品的维护迭代造成问题。


5、现在的软件设计,往往要求输出一份高保真的原型图、也会按照敏捷项目管理的流程对这份原型图建立持续更新的机制,确保原型图是需求的具体表达,但是产品语言并非统一语言,也许产品语言具有业务含义,但是由于不能指导开发者进行接口、类、持久层的设计,造成了代码与需求的割裂。在张逸老师的《领域驱动战术实践》提到他曾经使用dimension和metric两种不同的对象来定义一个维度对象,为代码造成了不必要的麻烦。我也曾经在一个项目,遇到过产品术语未能澄清,导致开发中使用style和theme两种截然不同的定义来定义与“风格”相关术语,为代码引入了不必要的纠结。



领域驱动设计引入了以下概念,但是我们无需在这篇文章中深刻理解这些概念的具体含义,我们只需知道,有这个东西。当我们开始按照领域驱动设计的方法设计一个系统时,按照前人整理的领域驱动的sample,往往就会将概念融汇贯通,达到更好的理解效果。


1、统一语言:定义好产品原型,需要建立统一语言。这是一种在内部和外部都能使用的规范化用语,包括UML、适当的图、一致性的描述、以及专业术语和术语对应的英文描述。


2、实体:在领域中可以通过标识进行唯一值定位的对象。


3、值对象:在领域中,从其他领域或某个实体中分离出只包含某些特定属性的对象。由于不具备唯一性特征,往往无需用于数据持久化。


4、聚合、聚合根:将具有相关性的对象聚合在一起,并以聚合根的形式统一对外提供访问方法和属性字段成员。


5、限界上下文:领域包含核心领域、子域和通用子域,而限界上下文则是一个具体业务的流程。每个限界上下文独立于其他限界上下文而存在,独立演进、功能完备。限界上下文的识别充满技术含量。


6、领域服务:包括仓储服务和工厂服务,前者负责实现对象与数据库的操作过程、封装了一系列数据库操作的方法;后者则侧重于对象的创建过程。个人认为从三层架构演进到领域驱动架构过程中,仓储服务是最接近于数据访问层的逻辑,也是让大部分领域驱动架构最终又回归到三层架构的一种通病。从对数据访问层中抽出对象、行为、数据访问,是战术设计的关键步骤。


领域驱动设计引入了一堆新的架构形式,包括经典的四层架构、EDA(事件驱动架构)、CQRS架构(命令查询职责分离)。而由于Evans的原书没有过分讨论如何识别领域,后来又有许多大佬在他的基础上进行了完善,提出了许多方法,包括名词、形容词、动词建模法、事件风暴、四色建模等方法,限于篇幅,且听下回分解。


640?wx_fmt=png


领域驱动设计,或许是解决这些问题的一剂良方,但也或许是开启了暗黑世界的大门。


概念晦涩难懂、程序员们不愿意开始思维变革、技术上可能存在不预期的坑、都可能让新方法的实践陷入一滩烂泥。还有许多人以为自己看懂了领域驱动设计(包括笔者),在往项目中运用时,总是有意无意的会被过程式代码的思维定式控制,让架构回退到三层架构。


由于微服务架构的兴起,让复杂系统的开发维护成为大家普遍关心的问题,使得Eric Evans于十五年前提出的这套理论,在今天绽放出了新的光芒。当然领域驱动设计仅仅只是众多面向对象编程的一种实践,通过领域驱动设计将UML等方法灵活的运用其中,通过打破原有数据库关系建模给代码造成的桎梏,让开发者能够真正的实现面向对象编程。


然而思维模式的转换并非易事,从过程式代码中,抽离出与对象有关的行为,远比理解这几个概念要复杂,这需要大量经验的积累。

640?wx_fmt=png

毋庸置疑,数据库建模驱动软件开发具有速度快、学习成本低的显著特点,在许多项目中,能在短期内可以给开发者带来许多便利;而应用领域驱动设计,则可以在更长的维护周期内,给软件维护带来实质性好处。


两种不同类型的开发模式,根据企业实际出发进行选择,还只是开始,但能真正运用好领域驱动设计或者UML、面向对象设计这种软件工程的美学思维来改造我们的系统,让系统绽放出更加璀璨的光芒,这才是软件设计的乐趣所在。

640?wx_fmt=png

长按订阅更多精彩▼

640?wx_fmt=jpeg

如有收获,点个在看,诚挚感谢640?wx_fmt=png


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

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

相关文章

redis为什么这么火该怎么用

最近一些人在介绍方案时,经常会出现redis这个词,于是很多小伙伴百度完redis也就觉得它是一个缓存,然后项目里面把数据丢进去完事,甚至有例如将实体属性拆分塞进redis hash里面的奇怪用法等等!原因是什么呢?…

.Net Core实现健康检查

ASP.NET Core 提供运行状况检查中间件和库,以用于报告应用基础结构组件的运行状况。运行状况探测可以由容器业务流程协调程和负载均衡器用于检查应用的状态。例如,容器业务流程协调程序可以通过停止滚动部署或重新启动容器来响应失败的运行状况检查。负载…

微软宣布加入 OpenJDK,看网上各派的热闹

微软宣布加入 OpenJDK 项目(https://www.oschina.net/news/111036/microsoft-to-participate-in-openidk),这两天在微信公众号里面有几种论调:上面这些都是Javaer的观点,在CSharper 对这件事情的反应更奇怪了&#xff…

这6点解释了罗永浩为什么要卖艺

01是的,我们的‘老赖又上热搜了。(ps:还是传统的语法,换了个人而已,味道有点改变)11 月 3 日下午,罗永浩因锤子科技的 375 万欠款被江苏丹阳法院限制高消费,他不得乘坐飞机头等舱、软卧、高铁等…

微软发布研究报告:企业数据管理普遍混乱,揭秘大数据分析趋势以及PowerBI的崛起机遇...

本文非常重要,忽略者责任自负。我们时常看到很多新闻说企业的数据分析或大数据如何如何高大上,但你自己感觉你自己所处的环境呢?很多小伙伴在群里真切的抱怨到:感觉是一坨祥云。为什么你看到的和你感受到的有如此巨大的反差&#…

Magicodes.Pay,打造开箱即用的统一支付库,已提供ABP模块封装

Magicodes.Pay,打造开箱即用的统一支付库,已提供ABP模块封装简介Magicodes.Pay,是心莱科技团队提供的统一支付库,相关库均使用.NET标准库编写,支持.NET Framework以及.NET Core。目前已提供Abp模块的封装,支…

在.NET Core 3.0中发布单个Exe文件(PublishSingleFile)

假设我有一个简单的“ Hello World”控制台应用程序,我想发送给朋友来运行。朋友没有安装.NET Core,所以我知道我需要为他构建一个独立的应用程序。很简单,我只需在项目目录中运行以下命令:dotnet publish -r win-x64 -c Release …

python import 问题

https://my.oschina.net/leejun2005/blog/109679 python中,每个py文件被称之为模块,每个具有__init__.py文件的目录被称为包。只要模块或者包所在的目录在sys.path中,就可以使用import 模块或import 包来使用。 如果想使用非当前模块中的…

.NET如何写正确的“抽奖”——数组乱序算法

.NET如何写正确的“抽奖”——数组乱序算法数组乱序算法常用于抽奖等生成临时数据操作。就拿年会抽奖来说,如果你的算法有任何瑕疵,造成了任何不公平,在年会现场 code review时,搞不好不能活着走出去。这个算法听起来很简单&#…

maximum mean discrepancy

http://blog.csdn.net/a1154761720/article/details/51516273 MMD:maximum mean discrepancy。最大平均差异。最先提出的时候用于双样本的检测(two-sample test)问题,用于判断两个分布p和q是否相同。它的基本假设是:如…

FineUICore基础版部署到docker实战

文 | 蒙古海军司令 合作者FineUI用了好多年,最近出了FineUICore版本,一直没时间是试一下docker,前几天买了一个腾讯云服务器,1核2g,装了centos7.6,开始的时候主要是整个个人博客,在腾讯云安装了…

2019全球Microsoft 365开发者训练营(北京站)

Microsoft365介绍:Microsoft365不仅仅是Office 365,它还包括Windows 10操作系统,以及诸多企业级移动和安全应用。它是一套可用于从小型到集团化企业的办公、协作、沟通的企业信息化解决方案。在2017年7月11日举行的Inspire年度合作伙伴大会上…

caffe/common.cu error: function atomicadd has already been defined

http://blog.csdn.NET/houqiqi/article/details/46469981 1, 下载matio(http://sourceforge.NET/projects/matio/) 2,,安装 $ tar zxf matio-X.Y.Z.tar.gz $ cd matio-X.Y.Z $ ./configure $ make $ make check $ make install sudo ldconfig (如果不执行&#x…

微软备战 RPA 市场,Power Platform,Ready GO!

最大赌注就在刚刚,微软在 Microsoft Ignite 2019 大会上,首席执行官萨蒂亚纳德拉(Satya Nadella)宣布了 Microsoft Power Platform 新平台的发布,并且说到:在与Azure合作方面,微软365&#xff0…

C# 8 新特性 - 只读struct成员

从C# 8开始,我们可以在struct的成员上使用readonly修饰符。 为struct的成员添加readonly修饰符就表示告诉编译器和开发者该成员不可以修改struct的状态。 看下面这个例子: 这里的ToString()方法不会修改Point这个struct的状态,所以我们可以在…

.NET Core 3.0 中间件 Middleware

中间件官网文档解释:中间件是一种装配到应用管道以处理请求和响应的软件 每个中间件:选择是否将请求传递到管道中的下一个组件。可在管道中的下一个组件前后执行工作。使用 IApplicationBuilder 创建中间件管道ASP.NET Core 请求管道包含一系列请求委托&…

重磅!微软发布 Visual Studio Online:Web 版 VS Code + 云开发环境

今天(北京时间 2019 年 11 月 4 日),在 Microsoft Ignite 2019 大会上,微软正式发布了 Visual Studio Online 公开预览版!概览Visual Studio Online 提供了由云服务支撑的开发环境。无论是一个长期项目,或是…

Ubuntu Linux将支持所有树莓派设备

Canonical 近期公开了对 Raspberry Pi 4 的支持计划,并表示将支持所有 Raspberry Pi 设备。随着 Ubuntu Server 19.10 版本的发布,Canonical 宣布正式支持 Raspberry Pi 4,Raspberry Pi 4 性能强大,但成本较低,可以在边…

面试官:你连RESTful都不知道我怎么敢要你?

加个“星标★”,每天11.50,好文必达全文约4000字,预计阅读时间8分钟面试官:了解RESTful吗?01 前言回归正题,看过很多RESTful相关的文章总结,参齐不齐,结合工作中的使用,非…

深入理解.NET Core的基元(二) - 共享框架

原文:Deep-dive into .NET Core primitives, part 2: the shared framework作者:Nate McMaster[1] 译文:深入理解.NET Core的基元(二) - 共享框架 作者:Lamond Lu本篇是之前翻译过的《深入理解.NET Core的基…