乐于分享
好东西不私藏

软件架构到底是什么?架构都要做哪些事情?

软件架构到底是什么?架构都要做哪些事情?
     当我们谈到架构的时候,脑子里都会想起一些架构领域的名词,比如SOA架构、微服务架构,MVC三层架构、六边形架构、洋葱架构、整洁架构,领域驱动模型设计(DDD)。这次名词在我们的脑子里都是散装的,停留在形式上,知其然不知其所以然。还有那些高端的架构图,什么用例/场景视图、逻辑视图、开发视图、进程视图、物理视图等,还有就是toGaf企业架构方法论,业务架构、数据架构、应用架构、技术架构等。架构相关的名词实在是太多了,这些名词及概念到底都是什么关系,有什么层次及递进关系呢?
1、软件架构到底要干什么?
搞不清楚这个问题,掌握再多的架构名词,你对于架构的理解也是混沌的,缺乏体系性。而要想弄明白软件架构到底要干什么,就必须理解软件架构的本质。软件架构的本质实际上就是一句话:

软件架构的本质就是管理复杂度

下面沿着这句话,从软件复杂度的起源,到复杂度管理过程中沉淀下来的一系列可复用的方法论,到架构模式、领域驱动设计到架构构件的复用。你会发现,上面提到的这些概念不是相互孤立的技术概念,而是一条“复杂度”治理的演进链。
如果大家了解过领域驱动设计(DDD),相信大家一定看过这本书,不知道大家有没有注意到这本书封面上的下一行小字"软件核心复杂性应对之道"。这是这本书作者埃里克·埃文斯(Eric Evans)说的,领域驱动设计就是为了应对软件核心复杂度的,是不是和我上边说的不应而和。其实领域驱动所应对的复杂度已经是上层的业务复杂度了,而复杂度贯穿软件架构的所有方面。下面我们就体系化的讲解下"软件核心复杂度"。
好吧,软件架构还没讲明白,现在又扯到软件核心复杂度了。其实软件架构是一门超级复杂的学科,一时半会说不明白,所以该扯还是得扯,要不不扯说不明白呀。
2、软件核心复杂度
1986年,软件工程大佬Frederick Brooks在其经典论文《没有银弹》提出了软件工程领域最有影响力的复杂度概念划分:本质复杂度与偶然复杂度。

本质复杂度(Essential Complexity) :定义软件要解决的问题本身固有的复杂性,不可消除,只能有效的进行管理,与使用的工具、语言、框架无关。

偶然复杂度(Accidental Complexity):则是软件在解决问题的过程中新引入的复杂性,可以消除,换更好的工具、方法或者框架就能改善。

2.1 本质复杂度:软件固有的挑战

从复杂度的概念提出已经40多年过去了,编程语言历经数代更迭,各类中间件与开发框架层出不穷,但这个论断至今依然成立:我们虽已大幅削减了偶然复杂度——从手写汇编迈向高级语言,从人工部署进化到容器化运维,但软件的本质复杂度却如同一头无法彻底消灭的“狼人”,始终如影随形。

Brooks 指出软件存在四种无法根除的本质属性,同时他在《人月神话》中,他还进一步揭示了人类组织方式本身如何加剧系统复杂性。这四大本质特性包括:

(1)复杂性(Complexity)软件可能是人类所构建的最复杂的知识产物之一。其内部几乎找不到完全相同的两个组件——一旦出现逻辑重复,工程师便会立即将其抽象为函数、类或模块。然而,随着系统规模扩大,各组件之间非线性的交互关系呈指数级增长,导致整体状态空间迅速膨胀,使得系统复杂度远超代码行数的线性增长。

(2)顺应性(Conformity)与物理世界遵循普适定律不同,软件必须不断迁就现实世界的混乱与非理性。它常常要对接五花八门的遗留系统、非标准接口,还要适应业务规则频繁变动甚至自相矛盾的要求。这种复杂性并非源于技术本身,而是来自外部社会结构、组织流程和商业生态的不规范性。

(3)可变性(Changeability)“软”字决定了软件的命运——它天生就处于持续演进之中。一栋大楼或一辆汽车交付后基本定型,而软件的上线往往只是生命周期的起点。无论是商业模式迭代、监管政策更新,还是用户需求变化,都迫使系统在其整个生命周期中不断被修改、扩展甚至重构。

(4)不可见性(Invisibility)软件缺乏物理形态,无法像建筑那样通过施工图纸、或像机械那样借助三维 CAD 模型直观呈现。其控制流、数据流与时序依赖错综交织,难以用单一视图完整表达。这种“不可见性”不仅阻碍开发者对系统全局的理解,也显著抬高了团队协作与知识传递的成本。

当这四大本质复杂性相互叠加,业务逻辑与技术实现深度纠缠,系统便极易滑向结构性失稳,甚至陷入难以维护的泥潭。(软件构建连接的涌现性)

2.2 偶然复杂度:源于解法而非问题本身

      与本质复杂度相对的是偶然复杂度——它并非来自问题域的内在特性,而是由我们当前采用的解决方案、技术手段或工具链的局限性所引入。过去四十年间,随着编程语言的代际演进以及中间件、框架生态的持续丰富,大量偶然复杂度已被显著削弱:从使用汇编语言到高级语言的跃迁,从手动部署到基于容器的自动化交付,都是对这类复杂度的有效消解。

若进一步细分,偶然复杂度可分为两类:

  • 工具性偶然复杂度:源于开发工具和编程语言本身的不成熟,例如调试体验差、构建流程繁琐、部署缺乏自动化等。得益于近年来 DevOps、IDE 智能化和云原生技术的发展,这类复杂度已大幅降低。

  • 结构性偶然复杂度:源自软件系统内部组织方式的缺陷。典型表现包括业务逻辑与技术细节混杂、模块职责边界不清、依赖关系错乱等。在这种架构下,哪怕是一个微小的需求变更,也可能触发广泛的连锁反应。团队成员对“代码应归属何处”缺乏共识,反映出工程结构缺乏清晰的设计原则。这种由不良架构引发的复杂性,虽非问题本身所需,却足以导致系统走向结构性失稳甚至崩溃。

基于Frederick Brooks的论文,我们可以得出一个比较精确的结论:

软件的本质复杂度无法消除,只能管理;软件的偶然复杂度无法归零,只能在工具和实践的演进中持续削减与转移。当偶然复杂度被压缩到足够低时,本质复杂度就成为了主要矛盾。

为什么偶然复杂度可以消减而始终无法消除?

因为每一代新技术在解决旧形态偶然复杂度的同时,都需要引入新的工具和组合,制造新形态的偶然复杂度。高级语言消除了汇编的繁琐,但引入了运行时开销和抽象泄漏;GC消除了手动内存管理的困难,但引入了Stop-the-World停顿的新问题;Docker消除了环境差异的困难,但引入了容器编排的新复杂度。

理解了这一点,"软件架构到底要干什么"的答案就浮出水面.

架构设计的终极使命,是在削减工具性和结构性偶然复杂度的同时,为本质复杂度提供有效的管理手段。

3、软件架构如何解决管理复杂度
从复杂度治理的角度看,过去几十年的软件工程史,就是三轮彼此递进的演进和治理过程。这个过程大致可以分为三个阶段:
3.1 工具性偶然复杂度的削减
    在软件工程发展的近50多年来,软件工程最大的进度其实是发生在工具层面。高级编程语言替代了机器码和汇编语言,好用的智能的代码编辑器IDE层出不穷,完全取代了手工编辑器编写代码,代码版本控制解决多人协作带来的混乱问题,自动化测试和CI大幅降低了软件交付成本。如今的代码开发人员不再需要关心底层寄存器的分配,不再需要手动进行内存的管理,也无需手工编译源代码,等待很长时间才可以看到编译结果。工具性偶然复杂度已经被压缩到非常低的水平。
    随着工具性偶然复杂度被极致的削减,结构性偶然复杂度渐渐的浮出水面。就像建楼一样,有了好用的工具,那么大楼的结构该如何组织,模块该如何划分及组合。
3.2 结构性偶然复杂度治理——架构的诞生及架构模式的演进
    随着软件研发相关工具的逐渐成熟,新的问题开始暴露出来:代码的结构应该如何组织?模块之间的以来关系应该如何设计?业务逻辑和技术细节应该怎么隔离?
    基于软件设计过程中遇到的以上问题,软件架构的概念逐渐诞生了。软件架构,实际上就是在解决软件复杂度的过程中,业界逐渐总结和提炼出的通用方法论。
    软件架构的演进,实际上经历了三个阶段:混沌阶段->雏形阶段->成熟阶段。其中,在这三个演进阶段中,架构模式就是在治理软件的结构性偶然复杂度过程中沉淀下来的方法论。
    架构模式(Architectural Pattern)的演进,实际上主要就是从两个方面展开的:(1)如何科学的划分软件模块的边界;(2)如何严格地控制软件模块之间的依赖方向。

从传统的MVC分三层架构到六边形架构,再到洋葱架构和整洁架构,每一代架构模式都在回答同一个核心问题:如何把容易变化的技术细节和相对稳定的核心业务规则进行拆分和隔离。

3.3 本质复杂度治理:领域驱动建模

架构模式解决了"代码放在哪里"和"模块间依赖方向"的问题,但它有一个很大的漏洞,就是软件在实现业务逻辑时,业务逻辑固有的复杂度如何管理。

保险订单、供应链、风险控制、产品营销、支付渠道、对账、履约,这些系统在研发过程中真正复杂的地方,不是“Service及Dao如何定义、放在哪里”,而是:

  • 业务边界到底在哪里?
  • 一个概念在不同语境中是不是同一个东西?
  • 哪些规则属于订单,哪些规则属于履约?
  • 哪些对象必须聚合在一起保持数据一致性?
  • 哪些变化应该通过事件传播,哪些应该在聚合内部消化?

    工具偶然复杂性、结构偶然复杂性及本质复杂性,这三个复杂性不是并列的,而是层层递进的关系:工具性偶然复杂度被削减后,结构性偶然复杂度浮出水面;结构性偶然复杂度被削减后,本质复杂度成为主要矛盾。而早些年业界都在关注工具偶然复杂性、结构偶然复杂性的治理方案,而对于业务本身的复杂性关注和治理的并不多,直到近十年国内才开始关注,也正是如此,领域驱动设计(DDD)才被大家熟悉并接受,而DDD的核心并不是为了解决偶然复杂性的,而是为了解决软件本质复杂性的。这也正Domain-DrivenDesign这几个英文单词最直接的含义,通过对业务领域的建模和分析,来指导技术实现的设计。在DDD之前,大家开发系统是自下而上的,先设计数据结构,然后从Dao层到Service层再到展示层。而DDD强调的是业务战略和战术,是自顶向下的设计,由顶层业务领域决定下层的技术边界划分及模块之间的依赖关系,包括数据结构的设计。

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