专栏首页DDDDDD应对复杂

DDD应对复杂

复杂

Eric Evans所著副标题--Tackling Complexity in the Heart of Software,对于简单系统其实没有必要使用DDD,只有在复杂系统中,才能体现DDD的价值

那么何为“复杂”,或者说为什么软件是复杂的呢?

即使是研究复杂系统的专家,如《复杂》一书的作者 Melanie Mitchell,都认为复杂没有一个明确得到公认的定义。不过,Melanie Mitchell 在接受 Ubiquity 杂志专访时,还是“勉为其难”地给出了一个通俗的复杂系统定义:由大量相互作用的部分组成的系统,与整个系统比起来,这些组成部分相对简单,没有中央控制,组成部分之间也没有全局性的通讯,并且组成部分的相互作用导致了复杂行为。

这个定义同样可以表达软件复杂度的特征。定义中的组成部分对于软件系统来说,就是我所谓的“设计单元”,基于粒度的不同可以是函数、对象、模块、组件和服务。这些设计单元相对简单,然而彼此之间的相互作用却导致了软件系统的复杂行为。

《管理3.0:培养和提升敏捷领导力》中从两个维度分析复杂:行为(预测能力)和结构(理解能力),分别举了实际生活中简单的例子

理解力维度分为 Simple 与 Comlicated 两个层次,预测能力维度则分为 Ordered、Complex 与 Chaotic 三个层次

在英文中,表示复杂的有Complicated和Complex

Simple = easily knowable. Complicated = not simple, but still knowable. Complex = not fully knowable, but reasonably predictable. Chaotic = neither knowable nor predictable.

参考复杂的含义,Complicated 与 Simple(简单)相对,意指非常难以理解,而 Complex 则介于 Ordered(有序的)与 Chaotic(混沌的)之间,认为在某种程度上可以预测,但会有很多出乎意料的事情发生

针对复杂的定义,分析角度以及应对的方法,绘制一张表格:

角度

维度

表象

应对

理解力

规模软件的需求决定了系统的规模。当需求呈现线性增长的趋势时,为了实现这些功能,软件规模也会以近似的速度增长

函数存在副作用,调用时可能对函数的结果作了隐含的假设; 类的职责繁多,不敢轻易修改,因为不知这种变化会影响到哪些模块; 热点代码被频繁变更,职责被包裹了一层又一层,没有清晰的边界; 在系统某个角落,隐藏着伺机而动的bug,当诱发条件具备时,则会让整条调用链瘫痪 不同的业务场景包含了不同的例外场景,每种例外场景的处理方式都各不相同; 同步处理与异步处理代码纠缠在一起,不可预知程序执行的顺序

分而治之,控制规模

结构结构之所以变得复杂,在多数情况下还是因为系统的质量属性决定的

代码没有显而易见的进入系统中的路径; 不存在一致性、不存在风格、也没有统一的概念能够将不同的部分组织在一起; 系统中的控制流让人觉得不舒服,无法预测; 系统中有太多的“坏味道”,整个代码库散发着腐烂的气味儿 数据很少放在使用它的地方,经常引入额外的巴罗克式缓存层,目的是试图让数据停留在更方便的地方

保持结构的清晰与一致所有设计质量高的软件系统都有相同的特征,就是拥有清晰直观且易于理解的结构。

预测能力

变化

在设计软件系统时,变化让我们患得患失,不知道如何把握系统设计的度。若拒绝对变化做出理智的预测,系统的设计会变得僵化,一旦变化发生,修改的成本会非常的大;若过于看重变化产生的影响,渴望涵盖一切变化的可能,一旦预期的变化不曾发生,我们之前为变化付出的成本就再也补偿不回来了。这就是所谓的“过度设计”

拥抱变化可进化性(Evolvability)可扩展性(Extensibility)可定制性(Customizability)

根据上面的总结,想起《架构与架构师》[1]中提到的架构师需要面对应用程序的两个层面需求:功能需求与非功能需求,功能性需求越多越大那对应的软件规模增长越快,可变性越大;非功能需求就要考虑质量属性,也就是整体结构要清晰,明确的规范约束,易于理解好维护。而这两个层面需求正好又对应着业务复杂度与技术复杂度

技术复杂度与业务复杂度并非完全独立,二者混合在一起产生的化合作用更让系统的复杂度变得不可预期,难以掌控。同时,技术的变化维度与业务的变化维度并不相同,产生变化的原因也不一致,倘若未能很好地界定二者之间的关系,系统架构缺乏清晰边界,会变得难以梳理。复杂度一旦增加,团队规模也将随之扩大,再揉以严峻的交付周期、人员流动等诸多因素,就好似将各种不稳定的易燃易爆气体混合在一个不可逃逸的密闭容器中一般,随时都可能爆炸:

DDD应对

这让我回想起了“结构化思维”,逻辑+套路

逻辑是一种能力,而套路是方法论,是经验。逻辑是道,方法论是术;逻辑可以学习很多思维模型理论,但套路有路径依赖,这也是去大厂的好处,可以接触到各种大牛,直接获取他们的经验和方法

DDD是如何应对这些复杂性的呢?

1.限界上下文分而治之,边界划分后,各子域相对整体规模就小了2.结构方面通过技术角度:分层架构、六边形架构...分离技术实现与业务逻辑3.变化,不再过程式编程,加强抽象领域模型,通过过程分解+对象模型,拆解并业务显示化,达到更好的复用性和易理解性

banq提出类似总结解决复杂性两种方法:拆解成松耦合的组件+使用容易让人明白的套路表达出来

拆解也就是引入了“领域或子域”以及“有界上下文”划分边界;再引入各种模式名词,比如聚合、实体、值对象、工厂、仓储、领域事件,让知晓这些模式的人能够一下子定位到功能对应实现的组件,随着人们逐步了解,对于理解DDD系统来说,就不再是复杂的

开发流程

我们在开发、运营、维护软件的过程中很多技术、做法、习惯和思想。软件工程把这些相关的技术和过程统一到一个体系中,叫“软件开发流程”,软件开发流程的目的是为了提高软件开发、运营和维护的效率,以及提升用户满意度、软件的可靠性和可维护性

发展了很多的流程,这儿复习一下最资深的瀑布模型

瀑布模型

瀑布模型(Waterfall Model)1970年温斯顿·罗伊斯(Winston Royce)提出了著名的“瀑布模型”,直到80年代早期,它一直是唯一被广泛采用的软件开发模型

当软件行业还在年幼时期,它从别的成熟行业(硬件设计,建筑工程)借用了不少经验和模型。在那些“硬”的行业中,产品大多数遵循[分析->设计->实现(制造)->销售->维护]这个流程

软件系统很难做到像建筑行业一样,程序员只要根据图样一步步实施就能完成项目。没人可以完整设计出一个大型项目的图样,这是瀑布软件工程方法的致命问题

比如A拥有一家跑车公司,可以给客户自定义生产跑车。有一天一土豪来到A的公司,跟A商谈了一个跑车项目,他们谈好了车型,材料,马力等等细节。之后,A带着团队做了6个月,做成了这架跑车,交给了土豪。可是土豪开了一天之后回来要求重做,原因是当讨论方案的时候,双方都忘记给跑车安尾灯了! 但是给跑车安装尾灯,需要重新设计车尾部,加上倒车灯,把车底拆开,安装线路,修改传动装置把倒车档和倒车灯联系起来

软件多变,而且很长周期后才让用户看到最终产品,反反复复

瀑布模型只能在产品的定义非常稳定;产品模块之间的接口、输入和输出能很好地用形式化的方法定义和验证

统一语言

软件行业相对其它传统行业有独特的难,同为构建,建筑工程在施工队进场开始构造之前,各种工程图样和材料都已经非常精确地准备到位,但在软件中没有足够时间收集所有需地,即使收集了,需求也不是从创建软件角度去描述

软件项目团队,从产品 到 开发,再到测试,应了那句不怕神一样的对手,就怕猪一样的队友,层层兜底,产品考虑不到的,开发帮兜底,开发不给力时,靠测试兜底,测试不走心的,那只能客户买单,名为兜底大队

项目开始,产品没有足够时间收集需求,但快速行动的重要性扎根程序员内心,没有足够信息来构建,可项目有交付期限,程序员只能进行创造性假设,填补产品经理留下的空白,只是为了保持项目不断推进。但产品经理或者客户经常改变主意,这意味着程序员对留白处的创建性假设经常夭折

项目实践需求与程序员的理解之间存在客观落差,但程序员负责最终完成项目,如果这种落差没有被注意或发现,就只能由程序员自己创造。这种创造是在没有指导或指示的情况下进行,当项目展示给客户时,客户才发现不是自己想要的,双方就容易争执

更多时候,程序员之所以在项目开始迟迟不亡友,是因为他们希望有人告诉该怎么做,但这种情况一般不会发生,这时就发挥自己的创造力,但这个创造有时会使项目偏离正确方向。如何发挥程序员创造力,同时又能保持项目的方向不被程序员带偏呢?

解决方法就是让程序员迟早参与创意过程,与业务专家、客户、产品经理一起参加头脑风暴会议,不断缩小双方思考或理解偏差。有逻辑性的正确设计会节省大量代码,因为编码实践来试错的代价是昂贵的。编码涉及大量技术细节,细到一个字段的字节数都需要考虑,一旦发现代码实现的功能完全不是客户要求的,就只能全部推倒重来。成千上万行代码被删除,好像它们没存在过,他们存在过的意义仅是让程序员明白:此路不通

程序员需要将业务知识的学习和思考结合起来,领域驱动核心就是多了一个领域层,这个领域是业务相关原语,随着业务价值变化而变化,通常每个企业的核心业务域是千差万别的,所以,在技术实现的时候,反复要问我们团队,我们多少比例的代码是针对业务本身,而业务本身对业务消费者有多大的价值?作为业务的技术实现方,一定要有手段通过量化方法,衡量你的业务价值,从而能精细化迭代升级

统一语言,并不是把从客户那里听到的内容翻译成程序自己的语言,减少误解,让客户更容易理解我们的草图,并且真正帮助纠正错误,帮助我们获取有关领域新知识,目标是创造可以被业务、技术和代码自身无歧义使用的共同术语

在PRD文档,设计文档,代码以及团队日常交流中,都使用同一套领域术语,极大地提升沟通和工作效率

简单理解起来的话,也就是把业务人员和开发人员的语言统一起来,用代码来感受一下大概就是:

userService.love(Jack, Rose) => Jack.love(Rose)companyService.hire(company,employee) => Company.hire(employee)

通过统一语言,领域驱动设计的核心是建立统一的领域模型。领域模型在软件架构中处于核心地位,软件开发过程中,必须以建立领域模型为中心,以保障领域模型的忠实体现,解决OOAD中OOA分析模型与OOD设计模型割裂的弊端

References

[1] 《架构与架构师》: http://www.zhuxingsheng.com/blog/architecture-and-architect.html

本文分享自微信公众号 - 码农戏码(coder-game),作者:朱先生

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-10-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 应对复杂性

    在《整洁架构》书中作者写到架构的主要目的是支持系统的生命周期。良好的架构使系统易于理解,易于开发,易于维护和易于部署。 最终目标是最小化系统的寿命成本并最大化程...

    码农戏码
  • 领域驱动应对业务复杂度

    之前的文章提到过,领域驱动设计分成战略层次和战术层次,战略层次我们讨论的很多了,接下来我们主要看下战术层次要搞哪些事情,以及领域驱动如何以架构的形式落地呢。

    春哥大魔王
  • 领域驱动设计-什么是领域驱动设计和怎么使用它

    这篇文章讨论领域驱动设计(DDD),DDD是建立在面向对象分析设计上开发软件的一种方法。 通过这篇文章我们解释什么是领域驱动设计,在现代开发周期中如何实现,使用...

    用户7657330
  • 解惑领域驱动设计的若干问题

    作者 | 张逸 最近重读Eric Evans的经典《领域驱动设计》,正如Eric提倡我们要去发现隐式概念一般,这次重读也让我发现了许多隐藏的DDD知识。恰好今...

    张逸
  • 领域驱动设计对软件复杂度的应对

    不管是因为规模与结构制造的理解力障碍,还是因为变化带来的预测能力问题,最终的决定因素还是因为需求。Eric Evans认为“很多应用程序最主要的复杂性并不在技术...

    张逸
  • DDD是如何解决复杂业务扩展问题?

    业务初期,功能比较简单,CRUD基本可以满足。但随着系统的不断演化,业务系统越来越复杂,各模块间有着千丝万缕的关系,如何提升其扩展性,避免牵一发而动全身,是我们...

    用户7676729
  • 读《中台架构与实现》

    最早是在极客时间知道欧创新老师的,我也是他的课程《DDD实战课》的订阅者,后来欧老师基于这门课程做更多的实践与思考,完成了《中台架构与实现:基于 DDD 和微服...

    oec2003
  • 公司终于决定放弃传统微服务,全面拥抱 DDD!

    前段时间,参加了一场闭门技术交流会,讨论的热点是微服务,话题集中在微服务架构拆分到底应该拆多细。

    Guide哥
  • DDD领域驱动设计理论篇 - 学习笔记

      在加入X公司后,开始了ASP.NET Core+Docker+Linux的技术实践,也开始了微服务架构的实践。在微服务的学习中,有一本微软官方出品的《.NE...

    Edison Zhou
  • 为什么在做微服务设计的时候需要DDD?

    因为在互联网时代,软件所面临的问题域比以往要复杂得多,这种复杂性来源于不断扩展的问题域自身,也来源于创新变化,以及这种规模性增长所带来的挑战。

    肉眼品世界
  • 为什么在做微服务设计的时候需要DDD?

    记得之前在规划和设计微服务架构的时候,给了我一个至今依然记忆深刻的提示:『你的设计蓝图里为什么没有看到DDD的影子呢?』

    xcbeyond
  • 微服务和DDD有什么关系

    但也会带来很多开发与运维上的负担。用DDD(领域驱动设计) 的思想去指导微服务的实践则成为比较好的方案。

    春哥大魔王
  • DDD战略战术

    1.DDD是什么2.复杂系统的特征3.DDD如何应对复杂系统4.模型概念5.软件开发流程

    码农戏码
  • DDD精粹:运用子域进行战略设计

    用户1682855
  • 如何应对混合云网络的复杂性?

    在经过一番艰苦努力的之后,我最终调试解决了一个非常棘手的混合云网络问题。 虚拟私有云(VPC)提供了一个包含免费虚拟机(VM)使用时间的培训项目,学生可以跟随一...

    静一
  • DDD开篇总结

    对于DDD的启蒙,不管是国内还是国外思维逻辑都是一样的。或者说如果你想写本关于DDD的书,大纲似乎是一样的

    码农戏码
  • 领域驱动落地方法论

    其实解决一个复杂领域系统的方式不只限于编码架构,我们同样需要从一些外溢的环节去考虑应对这种复杂度。

    春哥大魔王
  • 为何我用DDD重构了才刚上线的新项目?

    很多人初次接触DDD会有些反感,特别是对DO、DTO、PO这些对象转来转去反感,也有人质疑这样做影响性能。

    Java艺术
  • 从架构演进谈 DDD 兴起的原因以及与微服务的关系

    我们先不讨论DDD的定义, 先梳理一下DDD火起来的背景, 根据我学习的套路, 永远是为什么为先,再是解决什么问题,是什么东西, 最后如何使用。我们都知道这些年...

    孙玄@奈学教育

扫码关注云+社区

领取腾讯云代金券