


本质复杂度(Essential Complexity) :定义软件要解决的问题本身固有的复杂性,不可消除,只能有效的进行管理,与使用的工具、语言、框架无关。
偶然复杂度(Accidental Complexity):则是软件在解决问题的过程中新引入的复杂性,可以消除,换更好的工具、方法或者框架就能改善。
2.1 本质复杂度:软件固有的挑战
从复杂度的概念提出已经40多年过去了,编程语言历经数代更迭,各类中间件与开发框架层出不穷,但这个论断至今依然成立:我们虽已大幅削减了偶然复杂度——从手写汇编迈向高级语言,从人工部署进化到容器化运维,但软件的本质复杂度却如同一头无法彻底消灭的“狼人”,始终如影随形。
Brooks 指出软件存在四种无法根除的本质属性,同时他在《人月神话》中,他还进一步揭示了人类组织方式本身如何加剧系统复杂性。这四大本质特性包括:
(1)复杂性(Complexity)软件可能是人类所构建的最复杂的知识产物之一。其内部几乎找不到完全相同的两个组件——一旦出现逻辑重复,工程师便会立即将其抽象为函数、类或模块。然而,随着系统规模扩大,各组件之间非线性的交互关系呈指数级增长,导致整体状态空间迅速膨胀,使得系统复杂度远超代码行数的线性增长。
(2)顺应性(Conformity)与物理世界遵循普适定律不同,软件必须不断迁就现实世界的混乱与非理性。它常常要对接五花八门的遗留系统、非标准接口,还要适应业务规则频繁变动甚至自相矛盾的要求。这种复杂性并非源于技术本身,而是来自外部社会结构、组织流程和商业生态的不规范性。
(3)可变性(Changeability)“软”字决定了软件的命运——它天生就处于持续演进之中。一栋大楼或一辆汽车交付后基本定型,而软件的上线往往只是生命周期的起点。无论是商业模式迭代、监管政策更新,还是用户需求变化,都迫使系统在其整个生命周期中不断被修改、扩展甚至重构。
(4)不可见性(Invisibility)软件缺乏物理形态,无法像建筑那样通过施工图纸、或像机械那样借助三维 CAD 模型直观呈现。其控制流、数据流与时序依赖错综交织,难以用单一视图完整表达。这种“不可见性”不仅阻碍开发者对系统全局的理解,也显著抬高了团队协作与知识传递的成本。
当这四大本质复杂性相互叠加,业务逻辑与技术实现深度纠缠,系统便极易滑向结构性失稳,甚至陷入难以维护的泥潭。(软件构建连接的涌现性)
2.2 偶然复杂度:源于解法而非问题本身
与本质复杂度相对的是偶然复杂度——它并非来自问题域的内在特性,而是由我们当前采用的解决方案、技术手段或工具链的局限性所引入。过去四十年间,随着编程语言的代际演进以及中间件、框架生态的持续丰富,大量偶然复杂度已被显著削弱:从使用汇编语言到高级语言的跃迁,从手动部署到基于容器的自动化交付,都是对这类复杂度的有效消解。
若进一步细分,偶然复杂度可分为两类:
工具性偶然复杂度:源于开发工具和编程语言本身的不成熟,例如调试体验差、构建流程繁琐、部署缺乏自动化等。得益于近年来 DevOps、IDE 智能化和云原生技术的发展,这类复杂度已大幅降低。
结构性偶然复杂度:源自软件系统内部组织方式的缺陷。典型表现包括业务逻辑与技术细节混杂、模块职责边界不清、依赖关系错乱等。在这种架构下,哪怕是一个微小的需求变更,也可能触发广泛的连锁反应。团队成员对“代码应归属何处”缺乏共识,反映出工程结构缺乏清晰的设计原则。这种由不良架构引发的复杂性,虽非问题本身所需,却足以导致系统走向结构性失稳甚至崩溃。

软件的本质复杂度无法消除,只能管理;软件的偶然复杂度无法归零,只能在工具和实践的演进中持续削减与转移。当偶然复杂度被压缩到足够低时,本质复杂度就成为了主要矛盾。
为什么偶然复杂度可以消减而始终无法消除?
因为每一代新技术在解决旧形态偶然复杂度的同时,都需要引入新的工具和组合,制造新形态的偶然复杂度。高级语言消除了汇编的繁琐,但引入了运行时开销和抽象泄漏;GC消除了手动内存管理的困难,但引入了Stop-the-World停顿的新问题;Docker消除了环境差异的困难,但引入了容器编排的新复杂度。
理解了这一点,"软件架构到底要干什么"的答案就浮出水面.
“架构设计的终极使命,是在削减工具性和结构性偶然复杂度的同时,为本质复杂度提供有效的管理手段。

从传统的MVC分三层架构到六边形架构,再到洋葱架构和整洁架构,每一代架构模式都在回答同一个核心问题:如何把容易变化的技术细节和相对稳定的核心业务规则进行拆分和隔离。
3.3 本质复杂度治理:领域驱动建模
架构模式解决了"代码放在哪里"和"模块间依赖方向"的问题,但它有一个很大的漏洞,就是软件在实现业务逻辑时,业务逻辑固有的复杂度如何管理。
保险订单、供应链、风险控制、产品营销、支付渠道、对账、履约,这些系统在研发过程中真正复杂的地方,不是“Service及Dao如何定义、放在哪里”,而是:
业务边界到底在哪里? 一个概念在不同语境中是不是同一个东西? 哪些规则属于订单,哪些规则属于履约? 哪些对象必须聚合在一起保持数据一致性? 哪些变化应该通过事件传播,哪些应该在聚合内部消化?
工具偶然复杂性、结构偶然复杂性及本质复杂性,这三个复杂性不是并列的,而是层层递进的关系:工具性偶然复杂度被削减后,结构性偶然复杂度浮出水面;结构性偶然复杂度被削减后,本质复杂度成为主要矛盾。而早些年业界都在关注工具偶然复杂性、结构偶然复杂性的治理方案,而对于业务本身的复杂性关注和治理的并不多,直到近十年国内才开始关注,也正是如此,领域驱动设计(DDD)才被大家熟悉并接受,而DDD的核心并不是为了解决偶然复杂性的,而是为了解决软件本质复杂性的。这也正是Domain-DrivenDesign这几个英文单词最直接的含义,通过对业务领域的建模和分析,来指导技术实现的设计。在DDD之前,大家开发系统是自下而上的,先设计数据结构,然后从Dao层到Service层再到展示层。而DDD强调的是业务战略和战术,是自顶向下的设计,由顶层业务领域决定下层的技术边界划分及模块之间的依赖关系,包括数据结构的设计。

好了,由于篇幅问题,本节先写到这儿。本节主要从概念层面介绍了软件架构和软件核心复杂性的关系、软件复杂性的分类及各类复杂性在架构层面的解决方案。下节将继续对上文提到的各方法论进行详细描述。
夜雨聆风