首页 > 其他分享 >代码复用:DDD视角下的平衡艺术

代码复用:DDD视角下的平衡艺术

时间:2024-10-30 18:45:20浏览次数:4  
标签:竞争力 视角 代码 子域 复用 模块 DDD

代码复用:DDD视角下的平衡艺术 https://mp.weixin.qq.com/s/5gIBJByRZfNPbh6yjAvj9w

代码复用:DDD视角下的平衡艺术

原创 杜沁园(悬衡) 阿里技术 2024年10月11日 08:31 浙江

 

 


这是2024年的第76篇文章

( 本文阅读时间:15分钟 )




01



引言

刚工作时,代码写得不太好,师兄每次 CR 代码,总是会指着屏幕里的一坨代码说 “把它抽成一个类或函数”;“为什么呢?写在一起不是挺好的吗?” 我反问道;师兄老道地回答 “为了方便复用”;我仿佛若有所得,回到工位上把那些很长的代码全部抽象成了类和函数,感觉今天又有所成长。

 

但是随着工作经验的增加,我对此又产生了困惑。随着业务发展得越来越复杂,我当初写的那个类被大量复用,为了适应不同的场景,里面充满了 if...else...;最能代表复用的业务中台,因为分支太多,发布和开发无比复杂,很小的一个改动却需要拉一堆团队讨论。

 

所以类和函数的存在究竟是为了什么?只有站在更高的视角才能解决我的困惑,这也是本文的内容。

 

根据奥卡姆剃刀原则,本文其实用一句话就能概括, 它也是 《复杂软件设计之道》 中我最喜欢的一句话 :类和函数不是为了复用而存在,而是他们本来就 “应该” 在那里。

 

如果您对这句话已经意会了,可以直接跳到评论区聊一聊看法了。

 

下文中主要结合历史上各位软件泰斗的观点,分别从成本和效益角度聊聊 “应该” 一词的含义。



02



DRY vs 重复代码:谁更好吗?设计模式的 DRY 原则(Don't Repeat Yourself)让我们尽可能地不要编写重复的代码。
但是在复杂工程中导致的问题就是,DRY 的函数会被大量的地方引用,导致其内部逻辑需要考虑各种情况,逻辑及其复杂,修改风险也极高。
这么看来,DRY 也没有那么好,重复代码反而可以降低后续的修改风险,日后可以根据业务需要再进行灵活整合。
在 《架构整洁之道》中提到,“拖延决策” 也是优秀架构设计的特点之一。因为随着软件的开发和业务的迭代,我们掌握的信息越来越多,后期做出的决策肯定比项目早期的草率决定要靠谱。《复杂软件设计之道》中吐槽道:架构师们总是在只掌握 20% 信息的情况下,就已经做出了 80% 的决策。
大师们的原则常常是相互矛盾的,没有什么绝对的更好或者更坏。下表简要总结了下 DRY 与重复代码各自的优缺点:
图片

图片


从上表可以看出,重复代码和 DRY 很难说孰优孰劣,有时候费了半天劲抽取代码,反而系统复杂性更高了。符合设计原则的代码不一定是好代码,不符合设计原则的代码不一定是坏代码。
通过纯粹设计原则的角度是看不出来软件设计决策是否正确的,必须从更高的视角出发才行。

03



复用是一个权衡

我们常常被教育复用的好处,而忽视了复用的成本。为了复用一个代码模块:
  • 首先我需要知道可复用构件的存在
  • 然后了解其中的结构和接口
  • 对接模块的接口,并且测试无误
  • 最后,只是会用还不够,如果线上出现,我必须保证自己对它有足够的了解,可以去排查该模块的问题

而只要有成本的东西就是需要权衡的。没人愿意花费 10 元价格,只买回来一个价值 8 元的产品。
复用软件的好处众所周知,但我认为可以进一步拆分成两种:
  1. 降低开发成本。通过整合业务中台已有的支付,供应链等能力,可以快速支撑新的业务上线。
  2. 提升软件产品的核心竞争力。已有的模块经过线上检验,其中积累了过去成功的经验, 并且未来还会继续积累,直接复用能够大大提升产品的竞争力。

第一点是从成本角度,而第二点是从效益角度。
下文分别从这个两个角度与成本进行比较, 引出两位大师的观点,就能更好地得出软件复用的结论。

04



深浅模块:成本角度谈复用

谈到文件系统,或者数据库,应用肯定都是直接复用现有的开源软件,或者公司内专业团队定制的。不可能复制一份数据库代码到应用中。一方面是没这实力,更重要是不划算。
文件系统对上层提供了非常简单的文件模型,数据库对应用也提供了非常好理解的表模型。而他们的实现非常复杂,需要考虑并发,数据完整性,事务等一系列问题。相比理解他们的实现,学习模型和接口成本几乎可以忽略不计。

图片

学习 SQL 相比学习 数据库实现 的成本,从相关书籍的厚度上就能看出一二,更何况它们的阅读难度相差也很大。

 

上面的案例有共同的特点,即模块的接口很简单,但是提供的功能却是深刻的。这个时候复用就非常的合算。
这刚好就是 John Ousterhout 教授(Raft 的发明者)在其著作 《软件设计哲学》中提到 深模块 的概念。
深模块在简单的接口后隐藏了许多功能。深模块代表很好的抽象,其内部复杂性只有很小一部分对其用户可见。
其反例就是浅模块, 浅模块接口很复杂,提供的功能却不多。在项目中经常会下面这样的代码:
public void addParameter(List<String> params, String param) {    params.add(param);}

它接收两个参数,却只实现了一个最简单的列表增加元素功能,寻找和复用它的成本已经超过了复用的好处。
浅模块的接口复杂度和实现复杂度接近,与其去了解模块的接口,开发人员还不如自己重新实现一遍。

图片

 

图片

《软件设计哲学》书中的配图,方块的宽度代表模块接口的复杂程度,深度代表功能的深刻程度,接口应该越简单越好,功能应该越深刻越好。深模块就是接口简单但是功能深刻的模块。


05



塑造产品的核心竞争力:效益角度谈复用

什么情况下,复用能够提升产品的核心竞争力呢?
Supercell 游戏公司将之前的爆款中备受玩家欢迎的风格,素材和程序逻辑沉淀下来,通过复用之前积累,可以快速产出新的爆款。
钉钉的审批流程配置功能经过多年的迭代,操作习惯已经深入人心。后来钉钉又推出 CRM 应用,直接复用这套配置界面和逻辑, 虽然需要开发一些适配逻辑,但大大降低了用户的学习成本, 提升了竞争力。
上面的两个例子刚好就代表了两种提升产品核心竞争力的逻辑:
  1. 复用之前具有竞争力的技术模块,让过去的成功经验助力未来的产品成功
  2. 给用户提供一致的体验,考虑用户的使用习惯,降低学习成本

复用不同模块能取得效果的程度也是不同的,复用什么样模块更有可能获得上述两点效果呢?DDD 中对子域的划分或许能够给我们答案,在 DDD 中软件存在三种子域:
  • 核心子域
    • 特点:能够给公司带来核心竞争力的领域模块,拥有很高的复杂度和差异化价值
    • 案例:比如滴滴的司机调度算法,支付宝的交易系统,钉钉的 IM 系统等等
    • 复用策略:属于该子域的模块应该尽可能地复用, 将其竞争力也注入到其他产品,甚至投入精兵强将,提升其可扩展性,进一步拉开和竞争对手差距
  • 支持子域
    • 特点:用来支撑核心子域,但是不能带来竞争力
    • 案例:比如运营管理系统,后台排查系统等等
    • 复用策略:因为不能带来核心竞争力,不如各个业务根据自己需求,使用脚手架快速搭建,定制起来还更加方便
  • 通用子域
    • 特点:通用的业务或者技术问题领域, 比较复杂, 却不能给企业带来核心竞争力。好在一般有现成的解决方案,可以直接采购
    • 案例:比如财务系统,可以直接采购用友,金蝶;分库分表,消息队列可以直接使用开源软件,或者购买云上解决方案
    • 复用策略:尽可能复用,但是复用的目的与核心子域不同,主要是为了降低研发成本

以 DDD 中经典的货运管理系统为例(简化):
图片

相比对于业务的助力,复用的成本就显得微不足道了。
因此 DDD 要求技术和业务深度结合,如果不了解业务的话,单从设计原则角度,很难理解为什么要复用一个技术模块。
成功的设计来自对业务问题的深刻理解。最符合其业务子域的地方,才是类/函数 应该 在的地方。

06



总结

世上只有一种英雄主义,就是在认清生活真相之后依然热爱生活
工程师对技术也只有一种热爱,就是当发现任何技术都无法代替对业务的深入认知后,依旧热爱代码。
DDD 的思想和工具能够帮我们站在更高的视角,从业务分析的视角看待复用的成本和效益,帮助我们更好地做出决策。

图片

参考书目

[01] 《复杂软件设计之道》https://book.douban.com/subject/35216922/

[02] 《架构整洁之道》

https://book.douban.com/subject/30333919/

[03] 《软件设计哲学》

https://yingang.github.io/aposd-zh/

 

 

翻译

搜索

复制

标签:竞争力,视角,代码,子域,复用,模块,DDD
From: https://www.cnblogs.com/papering/p/18516346

相关文章

  • 为什么我越来越喜欢用DDD — DDD架构篇(1)
    HelloDDDDDD是一种软件设计方法,DDD是指导我们做软件工程设计的一种手段。它提供了用切割工程模型的各类技巧,如;领域、界限上下文、实体、值对象、聚合、工厂、仓储等。通过DDD的指导思想,我们可以在前期投入更多的时间,更加合理的规划出可持续迭代的工程设计。在DDD中有一套......
  • 0基础读顶会论文—面向应用的云工作负载预测:调查和新视角
    原文链接Abstract准确的工作负载预测对于云用户和提供商来说非常有价值,因为它可以有效指导许多实践,例如性能保证、降低成本和能耗优化。然而,由于工作负载的复杂性和动态性,云工作负载预测非常具有挑战性,并且已经提出了各种解决方案来增强预测行为,与现有调查不同,我们首次从一个新......
  • C++ 网络编程 IO多路复用、select、poll、epoll知识点总结
    1.什么是I/O多路复用?I/O多路复用(I/OMultiplexing)是一种编程技术,允许一个线程或进程同时管理多个I/O通道(如文件描述符、套接字等)。它使得单个进程能够在不使用多个线程或进程的情况下,同时处理多个I/O操作。这在网络编程和高性能服务器中尤为重要,因为它可以有效地利用系......
  • Allegro 16.6 布局时,模块如何进行复用
    模块复用:在原理图中有多个模块相似时,在PCB里面把这些模块处理为相同的布局,就可以使用模块复用功能。上图演示:1.在菜单栏打开PlacementEdit2.框选需要的模块,在某个器件上鼠标右键选择placereplicatecreate,再单击鼠标右键,选择done,单击鼠标左键,会弹出另存为对话框。 ......
  • H7-TOOL测试凌欧LKS32MC03x的骚操作玩法支持情况,不接硬件复位引脚,SWD上电100ms复用其
    【背景】这个骚操作,骚就骚在SWD接口引脚复用其他功能了,板子还没有把硬件复位引脚接出来,硬件复位引脚是专治这种场景的。虽然客户的程序搞了个上电后延迟100ms才复用,但JLINK,ULINK,STLINK,DAPLINK在MDK下都已经无法控制这个芯片下载,但使用第3方的烧录可以下载,且客户近期不方便提......
  • DDD之工程模型
    前言凡是做到架构师岗位的,都是具有一定技术思维敏感性的,不会主观评价好和坏,但能推演出业务与技术的迭代发展被动熵增与减熵增的意识。就像拿MVC与DDD对比,也能确切的感受到,在架构方面对比于单体应用的分布式架构,是要额外引入非常多的技术栈使用。但这些模块在MVC下并没有......
  • 阅读笔记一:《代码大全2》的宏观视角与编程艺术
    《代码大全2》是一部编程领域的经典之作,它不仅是一部技术指南,更是一部关于编程艺术的深刻探讨。在阅读这本书的过程中,我首先被其宏观的视角所吸引。作者并没有局限于某一种编程语言或技术框架,而是从更广泛的角度探讨了编程的基本原则、最佳实践和常见问题。书中强调了代码的可读性......
  • DDD话语批评之一:评“状态和事件本质相同”[全文]
    有位同学给我发了张逸著的《解构领域驱动设计》中的一页,让我评点一下。图1摘自《解构领域驱动设计》(张逸,2021)书中“状态和事件本质上是相同的”的观点真是令我“耳目一新”。那就针对这页书的内容来讲讲吧。我先介绍状态机的一些知识点,然后根据这些知识点来评价一下这页......
  • [答疑]是不是互联网更适合用DDD
    阿俊2021-11-413:11您发的第八章有一个调查一篇DDD文章提到几个类,我留意看过的文章和演讲,是像您说的那样!我还发现作者除了TW公司的人,还有大部分是互联网公司,而且都会提到微服务。我想问您的问题是,是不是互联网更适合用DDD,微服务架构更适合用DDD?UMLChina潘加宇你说的这个问......
  • redis高级篇之IO多路复用select方法简介 第174节答疑
    1、bitmap最大1024位,一个进程最多只能处理1024个客户端2、&rset不可重用,每次socket有数据就相应的位会被置位3、文件描述符数组拷贝到了内核态(只不过无系统调用切换上下文的开销。(内核层可优化为异步事件通知)),仍然有开销。select调用需要传入fd数组,需要拷贝一份到内核,高......