DDD学习笔记2-从软件复杂度开始讲起

学习资源来自Gitchat上张逸的《领域驱动设计实践》

Eric Evans 的经典著作《领域驱动设计》的副标题为“软件核心复杂性应对之道”,这说明了 Eric 对领域驱动设计的定位就是应对软件开发的复杂度。Eric 甚至认为:“领域驱动设计只有应用在大型项目上才能产生最大的收益”。因此,选择领域驱动设计,就是要与软件系统的复杂作一番殊死拼搏,以降低软件复杂度为己任。那么,是什么造成了软件的复杂呢?

规模

软件的需求决定了系统的规模。当需求呈现线性增长的趋势时,为了实现这些功能,软件规模也会以近似的速度增长。由于需求不可能做到完全独立,导致出现相互影响相互依赖的关系,修改一处就会牵一发而动全身。就好似城市的一条道路因为施工需要临时关闭,此路不通,通行的车辆只能改道绕行,这又导致了其他原本已经饱和的道路,因为涌入更多车辆,超出道路的负载从而变得更加拥堵,这种拥堵现象又会顺势向这些道路的其他分叉道路蔓延,形成一种辐射效应的拥堵现象。

软件开发的拥堵现象或许更严重:

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

当需求增多时,软件系统的规模也会增大,且这种增长趋势并非线性增长,会更加陡峭。倘若需求还产生了事先未曾预料到的变化,我们又没有足够的风险应对措施,在时间紧迫的情况下,难免会对设计做出妥协,头疼医头、脚疼医脚,在系统的各个地方打上补丁,从而欠下技术债(Technical Debt)。当技术债务越欠越多,累计到某个临界点时,就会由量变引起质变,整个软件系统的复杂度达到巅峰,步入衰亡的老年期,成为“可怕”的遗留系统。正如饲养场的“奶牛规则”:奶牛逐渐衰老,最终无奶可挤;然而与此同时,饲养成本却在上升。

结构

不知大家是否去过迷宫?相似而回旋繁复的结构使得本来封闭狭小的空间被魔法般地扩展为一个无限的空间,变得无穷大,仿佛这空间被安置了一个循环,倘若没有找到正确的退出条件,循环就会无休无止,永远无法退出。许多规模较小却格外复杂的软件系统,就好似这样的一座迷宫。

此时,结构成了决定系统复杂度的关键因素。

结构之所以变得复杂,在多数情况下还是因为系统的质量属性决定的。例如,我们需要满足高性能、高并发的需求,就需要考虑在系统中引入缓存、并行处理、CDN、异步消息以及支持分区的可伸缩结构。倘若我们需要支持对海量数据的高效分析,就得考虑这些海量数据该如何分布存储,并如何有效地利用各个节点的内存与 CPU 资源执行运算。

从系统结构的视角看,单体架构一定比微服务架构更简单,更便于掌控,正如单细胞生物比人体的生理结构要简单数百倍;那么,为何还有这么多软件组织开始清算自己的软件资产,花费大量人力物力对现有的单体架构进行重构,走向微服务化?究其主因,不还是系统的质量属性在作祟吗?

纵观软件设计的历史,不是分久必合、合久必分,而是不断拆分、继续拆分、持续拆分的微型化过程。分解的软件元素不可能单兵作战,怎么协同、怎么通信,就成为了系统分解后面临的主要问题。如果没有控制好,这些问题固有的复杂度甚至会在某些场景下超过因为分解给我们带来的收益。

变化

未来总会出现不可预测的变化,这种不可预测性带来的复杂度,使得我们产生畏惧,因为我们不知道何时会发生变化,变化的方向又会走向哪里,这就导致心理滋生一种仿若失重一般的感觉。变化让事物失去控制,受到事物牵扯的我们会感到惶恐不安。

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

从需求的角度讲,变化可能来自业务需求,也可能来自质量属性。以对系统架构的影响而言,尤以后者为甚,因为它可能牵涉到整个基础架构的变更。George Fairbanks在《恰如其分的软件架构》一书中介绍了邮件托管服务公司 RackSpace 的日志架构变迁,业务功能没有任何变化,却因为邮件数量的持续增长,为满足性能需求,架构经历了三个完全不同系统的变迁:从最初的本地日志文件,到中央数据库,再到基于 HDFS 的分布式存储,整个系统几乎发生了颠覆性的变化。这并非 RackSpace 的设计师欠缺设计能力,而是在公司草创之初,他们没有能够高瞻远瞩地预见到客户数量的增长,导致日志数据增多,以至于超出了已有系统支持的能力范围。俗话说:“事后诸葛亮”,当我们在对一个软件系统的架构设计进行复盘时,总会发现许多设计决策是如此的愚昧。殊不知这并非愚昧,而是在设计当初,我们手中掌握的筹码不足以让自己赢下这场面对未来的战争罢了。

如果将软件系统中我们自己开发的部分都划归为需求的范畴,那么还有一种变化,则是因为我们依赖的第三方库、框架或平台、甚至语言版本的变化带来的连锁反应。

如果变化是不可预测的,那么软件系统也会变得不可预测。一方面我们要尽可能地控制变化,至少要将变化产生的影响限制在较小的空间范围内;另一方面又要保证系统不会因为满足可扩展性而变得更加复杂,最后背上过度设计的坏名声。软件设计者们就像走在高空钢缆的技巧挑战者,惊险地调整重心以维持行动的平衡。故而,变化之难,在于如何平衡。

你可能感兴趣的:(DDD学习笔记2-从软件复杂度开始讲起)