无论我们怎么努力,软件依然变得越来越难以改变。由于各种原因,软件的组成部分不容易变更,而且随着时间推移变得愈发脆弱和难以操作。软件项目的变更通常是由于对功能或范围做了重新评估而导致的,但是还有一些变化是架构师和长期规划者无法控制的。尽管架构师喜欢为未来做战略性规划,但不断变化的软件开发环境使这一切变得困难重重。既然变化是必然的,那么我们就只能因势利导地来利用它。
生物世界中,环境因自然因素和人为因素而不断变化。例如,20 世纪 30 年代初,澳大利亚的甘蔗受到甲虫危害,导致甘蔗作物严重减产,利润大减。1935 年 6 月,作为应对措施,当时的甘蔗实验站管理总局引入了甘蔗蟾蜍来捕食甲虫,这种蟾蜍原本只产于中美洲和南美洲。短暂喂养后,1935 年 7 月和 8 月在昆士兰州北部投放了甘蔗蟾蜍。因为它们的皮肤有剧毒,并且在当地没有天敌,很快这种蟾蜍就泛滥成灾了。如今,澳大利亚的甘蔗蟾蜍大约有 2 亿只。这件事告诉我们:向高度动态的(生态)系统中引入变化,可能会产生无法预料的结果。
软件开发体系由所有的工具、框架、库以及最佳实践(软件开发领域的技术积累)构成。和生态系统一样,软件开发体系实现了平衡,开发人员能够理解这个体系并为其添砖加瓦。然而,这种平衡是动态的,随着新事物不断出现,平衡不断被打破和重建。想象一个脚踏独轮车,手里还拿着盒子的人。他是 动态的 ,因为他需要不断调整来保持挺立;他又是 平衡的 ,因为他保持着身体平衡。在软件开发体系中,每一项创新或新实践都可能打破现状,迫使系统重新建立平衡。就好比我们不断地将更多的盒子抛向骑独轮车的人,迫使他不断寻求新的平衡。
架构师在很多方面都和这个倒霉的独轮车手相似,不断地平衡以适应环境变化。持续交付这项工程实践使得这个平衡过程有了结构性的转变,它将过去孤立的功能(例如运维)合并到了软件开发的生命周期中,这让我们对 变化 的含义有了新的认识。企业级架构师不能再依赖静态的五年计划了,因为整个软件开发体系在不断变化,任何一个长期计划都可能变得毫无意义。
即便对经验丰富的实践者来说,颠覆性的创新也是难以预测的。比如,像 Docker 这样的容器化技术的崛起就是一个不可预知的行业转变。但我们仍然可以通过一系列小的演进找到一些蛛丝马迹。以前,操作系统、应用服务器和其他基础设施都是商品,需要斥巨资购买使用许可。当时许多架构设计着重于高效利用共享资源。渐渐地,Linux 变得足以支撑企业及应用,使得购买操作系统的 费用 降为零。接下来,通过 Puppet 和 Chef 等工具自动配置服务器的 DevOps 实践使得 Linux 运维工作 也不再需要成本。一旦开发环境免费并得到广泛应用,势必朝着更加通用和便携的方向发展,于是 Docker 应运而生。但如果没有之前所有的演进过程,容器化就不会发生。
我们所使用的编程平台也在持续演进。新的编程语言提供了更好的应用编程接口(API),提高了对新问题的灵活性和适用性。新的编程语言还提供了不同的范式和概念。例如,引入 Java 替代 C++,降低了编写网络代码的难度并改善了内存管理。回顾过去的 20 年,很多语言一直在持续改进它们的 API,与此同时,新的编程语言往往用于解决新的问题。编程语言的演变如图 1-2 所示。
图1-2 流行编程语言的演进过程
无论是在软件开发的哪个方面,比如编程平台、编程语言、运维环境、持久化技术等,我们都知道改变会持续发生。虽然无法预测技术或领域格局何时会改变,或哪些变化会持续下去,但我们清楚改变是不可避免的。因此,我们应该在构建系统的过程中对这一点保持清醒的认识。
如果整个体系持续地以出乎意料的方式发生变化,预测变化就变得不可能了,那么用什么来 替代 固定计划呢?企业架构师和开发人员必须学会适应变化。做长期计划有一个隐含的原因是财务上的考虑,因为以前软件变更的成本很高。但是,现代工程实践通过自动化和其他先进实践(例如 DevOps)降低了软件变更的成本。
多年来,一些聪明的开发人员发现系统的某些部分相对而言更难修改。这便是将 软件架构 定义为“将来难以变更的部分”的原因,这个省事的定义简单地区分了软件系统中真的难以修改的部分和可以轻松修改的部分。但这个定义依然不免走进了盲区,因为开发者预先假设“变更是困难的”,这变成了一个自证式的预言。
几年前,一些富有创新精神的架构师用新的视角审视了“将来难以变更”的问题。使架构具有可变性会怎样呢?换句话说,如果 易于改变 是架构的基本原则,那么变更将不再困难。反过来,使架构具备演进能力会导致一组全新的行为出现,进而再次打破整个体系的平衡。
即使环境不改变,架构特征出现磨损该怎么办?架构师设计出架构,将其置于纷乱的现实世界,基于架构执行各项事务。架构师要如何保护他们定义的重要部分呢?
有一种不幸的退化叫作架构 比特衰减 ,它在很多组织中均有发生。架构师选择特定的架构模式来满足业务需求及让系统具备某些能力,但这些特征常常意外地随着时间推移而退化。例如,架构师构建了一个包含顶部展现层、底部持久层和一些中间层的分层架构。负责报表功能的开发人员出于性能上的考虑经常会要求绕过中间层,直接从展现层访问持久层。架构师通过分层来隔离变化。而后开发人员绕过这些层,不仅增加了耦合,还使得分层变得毫无价值。
定义了那些重要的架构特征后,架构师如何 保护 这些特征不磨损呢?答案是添加 演进能力 作为新的架构特征,使其在系统演进时保护其他特征。比如,架构师追求架构设计的高伸缩性,但不希望在系统演进时削弱该特征。因此, 演进能力 是一种元特征和保护其他所有架构特征的架构封装器。
本书将阐明演进式架构的另一个作用,即它是一种为架构的重要特征提供保护的机制。我们探寻 持续架构 背后的理念。 持续架构 指构建架构的过程没有最终状态,它会随着软件开发体系的不断变化而演进,并保护重要的架构特征。我们不会尝试定义整个软件架构,因为已经存在很多定义了。我们通过引入 时间 和 变化 作为头等架构元素来扩展当前的定义。
我们对演进式架构的定义如下。
演进式架构支持跨多个维度的引导性增量变更。