架构设计三原则

news2025/6/19 10:47:38

作为程序员,很多人都希望成为一名架构师,但并非简单地通过编程技能就能够达成这一目标。事实上,优秀的程序员和架构师之间存在一个明显的鸿沟——不确定性。

编程的本质是确定性的,也就是说,对于同一段代码,无论由谁编写,在何时执行,其结果应该是确定的(尽管有可能存在bug)。相比之下,架构设计本质上是不确定的。同一系统,不同公司的架构可能存在较大的差异,但最终都能正常运转。在面对多种可能性时,架构师需要进行选择,而这种选择往往会让人陷入两难的境地。

例如:

是否选择业界最先进的技术,还是选择团队目前最熟悉的技术? 是否选择Google的Angular或Facebook的React? 是否选择MySQL或MongoDB? 对于这些问题,架构师需要依赖自己的经验和直觉进行决策,因为架构设计领域并没有一套通用的规范来指导架构师。但是,通过研究架构设计的发展历史和多个公司的架构发展过程,可以发现一些共性原则,即合适原则、简单原则和演化原则。在遵循这些原则的基础上,架构师可以做出最好的选择,克服不确定性。

合适原则

合适原则宣言:“合适优于业界领先”。

很多优秀的技术人员都怀有强烈的技术情结,他们总想通过挑战自我,实现甚至超越业界领先水平,从而在年终KPI绩效总结中留下自己的印记。然而,这种做法往往会导致失败。在互联网行业,我见过许多“亿级用户平台”的失败案例,例如某个几个人规模的业务团队,雄心勃勃地想要和腾讯QQ一较高下,但最终却以失败告终。为什么会这样呢?

实现任何梦想都需要脚踏实地的付出。这里的“脚踏实地”主要体现在以下几个方面。

1.将军难打无兵之仗。 大公司的分工比较细,每个小系统都可能由一个小组负责。但在大部分公司,整个研发团队可能只有100多人,某个业务团队可能只有十几个人。在这种情况下,想要完成类似于几十人团队才能完成的事情,并且还要做得更好,难度可想而知。

2.罗马不是一天建成的。 业界领先的很多方案并不是一群天才某个时期灵机一动就做出来的。它们经过了数年的发展才逐步完善和初具规模。如果没有足够的积累和历练,靠拍脑袋或者头脑风暴是无法和真正的实战相比的。

3.冰山下面才是关键。 业界领先的方案大多是“逼”出来的。随着业务的发展和量变导致质变,新的问题出现了,已有的方式无法应对这些问题,需要用新的方案来解决。通过不断的创新和尝试,业界领先的方案才得以形成。

如果没有类似于腾讯那样海量用户积累、大量的人力资源、优秀的业务场景,想要建立一个“亿级用户平台”就注定会失败。真正优秀的架构应该是在企业当前的人力、条件、业务等各种约束下设计出来的,能够将资源合理地整合在一起并发挥出最大的功效,并且能够快速落地。许多BAT公司的架构师到了小公司或创业团队却无法创造出成果,因为他们缺乏大公司平台、资源和积累的支持,他们只是生搬硬套大公司的做法,这样的做法成功的概率非常低。

因此,真正优秀的架构设计应该是在实际条件下的切实可行的设计,将资源合理利用,并能够快速落地。成功的架构设计需要充分考虑公司当前的资源、团队能力和业务需求,不断优化和改进,不断学习和尝试新的方法和技术,以适应日益变化的市场和业务环境。同时,我们也需要认识到,技术的发展需要时间,需要不断的积累和经验,只有在大量的实践中才能不断提升自己的技术水平。

简单原则

简单原则宣言:“简单优于复杂”。

确实,在软件领域,过度追求复杂性往往会导致设计出来的系统难以维护、扩展和调试,进而影响整个团队的工作效率。因此,我们需要转变思路,不要将复杂性作为评价架构的主要指标,而是要以系统的实际需求和问题为出发点,遵循合适原则、简单原则、演化原则,以最小的复杂度满足系统的需求。简单来说,就是要“追求简单”。

追求简单不等于简单粗暴,而是在不降低系统性能、稳定性和可扩展性的前提下,以最少的设计和实现来满足需求。这需要架构师具备全面的技术能力和对系统的深刻理解,能够对复杂性进行有效的把控和分解,从而将系统的设计和实现变得更加简单明了。

此外,简单也不意味着架构师可以忽略细节和考虑不周。实际上,追求简单需要更加注重细节和全局的把握,要通过精心的设计和实现来避免各种潜在的问题和风险。只有在这样的基础上,才能够设计出真正合适的、简单的、可演化的软件架构。

软件领域的复杂性体现在两个方面:

1.结构的复杂性

结构复杂的系统几乎毫无例外具备两个特点:

  • 组成复杂系统的组件数量更多;

  • 同时这些组件之间的关系也更加复杂。

我以图形的方式来说明复杂性:

2个组件组成的系统:

alt

3个组件组成的系统:

alt

4个组件组成的系统:

alt

5个组件组成的系统:

alt

为了避免这些问题,我们需要尽量简化架构。简化架构并不意味着牺牲可用性或者功能,而是在保证功能和可用性的前提下,尽量减少组件的数量和组件之间的关系复杂度。

一个好的架构需要在多方面权衡,如系统可用性、开发效率、维护成本、可扩展性等等。因此,在进行架构设计时,需要从多个角度出发,考虑系统的实际需求和限制条件,逐步迭代优化设计方案,而不是一开始就试图设计出最完美的方案。

另外,一个好的架构设计需要在实践中不断迭代和优化。系统运行过程中会出现各种各样的问题,需要不断地对架构进行调整和优化。同时,随着业务的发展和变化,架构设计也需要随之变化,及时做出调整和优化,才能够保证系统的稳定性和可持续发展。

2.逻辑的复杂性

意识到结构的复杂性后,我们的第一反应可能就是“降低组件数量”,毕竟组件数量越少,系统结构越简。最简单的结构当然就是整个系统只有一个组件,即系统本身,所有的功能和逻辑都在这一个组件中实现。

不幸的是,这样做是行不通的,原因在于除了结构的复杂性,还有逻辑的复杂性,即如果某个组件的逻辑太复杂,一样会带来各种问题。

逻辑复杂的组件,一个典型特征就是单个组件承担了太多的功能。以电商业务为例,常见的功能有:商品管理、商品搜索、商品展示、订单管理、用户管理、支付、发货、客服……把这些功能全部在一个组件中实现,就是典型的逻辑复杂性。

逻辑复杂几乎会导致软件工程的每个环节都有问题,假设现在淘宝将这些功能全部在单一的组件中实现,可以想象一下这个恐怖的场景:

  • 系统会很庞大,可能是上百万、上千万的代码规模,“clone”一次代码要30分钟。

  • 几十、上百人维护这一套代码,某个“菜鸟”不小心改了一行代码,导致整站崩溃。

  • 需求像雪片般飞来,为了应对,开几十个代码分支,然后各种分支合并、各种分支覆盖。

  • 产品、研发、测试、项目管理不停地开会讨论版本计划,协调资源,解决冲突。

  • 版本太多,每天都要上线几十个版本,系统每隔1个小时重启一次。

  • 线上运行出现故障,几十个人扑上去定位和处理,一间小黑屋都装不下所有人,整个办公区闹翻天。

  • ……

不用多说,肯定谁都无法忍受这样的场景。

但是,为什么复杂的电路就意味更强大的功能,而复杂的架构却有很多问题呢?根本原因在于电路一旦设计好后进入生产,就不会再变,复杂性只是在设计时带来影响;而一个软件系统在投入使用后,后续还有源源不断的需求要实现,因此要不断地修改系统,复杂性在整个系统生命周期中都有很大影响。

功能复杂的组件,另外一个典型特征就是采用了复杂的算法。复杂算法导致的问题主要是难以理解,进而导致难以实现、难以修改,并且出了问题难以快速解决。

以ZooKeeper为例,ZooKeeper本身的功能主要就是选举,为了实现分布式下的选举,采用了ZAB协议,所以ZooKeeper功能虽然相对简单,但系统实现却比较复杂。相比之下,etcd就要简单一些,因为etcd采用的是Raft算法,相比ZAB协议,Raft算法更加容易理解,更加容易实现。

综合前面的分析,我们可以看到,无论是结构的复杂性,还是逻辑的复杂性,都会存在各种问题,所以架构设计时如果简单的方案和复杂的方案都可以满足需求,最好选择简单的方案。《UNIX编程艺术》总结的KISS(Keep It Simple, Stupid!)原则一样适应于架构设计。

在架构设计中,我们应该遵循KISS原则,尽量让架构设计简单而易于理解和维护。另外,我们还可以通过以下几种方式来避免复杂性问题:

将系统分解为小的组件。每个组件都应该尽可能地简单,只实现一个功能。这样可以避免单个组件变得过于复杂,同时也方便组件的维护和升级。

  • 采用标准化的组件。通过采用标准化的组件,可以避免重新发明轮子,也可以减少对复杂组件的依赖。

  • 避免过度设计。过度设计往往会导致代码过于复杂,难以维护。在设计时,应该尽量避免不必要的设计,只实现必要的功能。

  • 尽量采用简单的算法。尽量使用简单的算法,可以使系统更加易于理解和维护。对于一些比较复杂的算法,可以考虑采用现成的库,避免重复造轮子。

  • 借鉴已有的成功经验。在架构设计时,可以参考已有的成功案例,借鉴其中的经验和教训,避免犯类似的错误。

总之,架构设计是一门艺术,需要不断地思考和实践,才能设计出简单、易于理解和维护的系统。

演化原则

演化原则宣言:“演化优于一步到位”。

软件架构从字面意思理解和建筑结构非常类似,事实上“架构”这个词就是建筑领域的专业名词,维基百科对“软件架构”的定义中有一段话描述了这种相似性:

从和目的、主题、材料和结构的联系上来说,软件架构可以和建筑物的架构相比拟。

例如,软件架构描述的是一个软件系统的结构,包括各个模块,以及这些模块的关系;建筑架构描述的是一幢建筑的结构,包括各个部件,以及这些部件如何有机地组成成一幢完美的建筑。

然而,字面意思上的相似性却掩盖了一个本质上的差异:建筑一旦完成(甚至一旦开建)就不可再变,而软件却需要根据业务的发展不断地变化!

  • 古埃及的吉萨大金字塔,4000多年前完成的,到现在还是当初的架构。

  • 中国的明长城,600多年前完成的,现在保存下来的长城还是当年的结构。

  • 美国白宫,1800年建成,200年来进行了几次扩展,但整体结构并无变化,只是在旁边的空地扩建或者改造内部的布局。

对比一下,我们来看看软件架构。

Windows系统的发展历史:

alt

如果对比Windows 8的架构和Windows 1.0的架构,就会发现它们其实是两个不同的系统了!

Android的发展历史:

alt

( http://www.dappworld.com/wp-content/uploads/2015/09/Android-History-Dappworld.jpg)

同样,Android 6.0和Android 1.6的差异也很大。

软件架构需要根据业务发展不断变化,所以在做架构设计时应该采用迭代的方式,而不是一步到位。这样做的好处是,可以让架构师随着业务的变化逐渐深入了解业务需求,同时也可以避免设计出过度复杂、不切实际的方案。

另外,预测和分析的确是不可靠的,但是我们可以采用一些技术手段来帮助我们应对变化。比如,可以采用微服务架构,将系统划分成若干个小服务,每个服务都可以独立开发、测试、部署和扩展。这样一来,当业务需求发生变化时,只需要修改涉及到的服务,而不用对整个系统进行修改。此外,还可以采用容器技术,通过容器化应用程序来实现快速部署和扩展,以应对业务变化带来的挑战。

总之,软件架构需要根据业务发展不断变化,我们应该采用迭代的方式来设计架构,并采用一些技术手段来帮助我们应对变化。同时,我们也需要明确,软件架构永远不可能一劳永逸,只有不断地学习和改进,才能保持软件架构的健康和持续性。

考虑到软件架构需要根据业务发展不断变化这个本质特点, 软件架构设计其实更加类似于大自然“设计”一个生物,通过演化让生物适应环境,逐步变得更加强大:

  • 首先,生物要适应当时的环境。

  • 其次,生物需要不断地繁殖,将有利的基因传递下去,将不利的基因剔除或者修复。

  • 第三,当环境变化时,生物要能够快速改变以适应环境变化;如果生物无法调整就被自然淘汰;新的生物会保留一部分原来被淘汰生物的基因。

软件架构设计同样是类似的过程:

  • 首先,设计出来的架构要满足当时的业务需要。

  • 其次,架构要不断地在实际应用过程中迭代,保留优秀的设计,修复有缺陷的设计,改正错误的设计,去掉无用的设计,使得架构逐渐完善。

  • 第三,当业务发生变化时,架构要扩展、重构,甚至重写;代码也许会重写,但有价值的经验、教训、逻辑、设计等(类似生物体内的基因)却可以在新架构中延续。

因此,架构师在设计软件架构时,需要始终关注业务需求,不断调整和改进架构以适应业务的发展。架构设计不应该是一次性的,而是一个持续演化的过程。同时,要注重简单性,避免过度复杂的架构设计,这样才能更好地满足业务需求,提高系统的可靠性和可维护性。

小结

以上的这三个原则都是为了应对“不确定性”而提出的,帮助架构师在复杂、快速变化的业务环境下做出更好的决策。

需要注意的是,这些原则并不是刻板的规则,而是指导思想。在实践中,架构师需要根据具体情况进行权衡和调整。有时候,一个复杂的方案可能确实更适合当前的业务需求;有时候,一个一步到位的方案也可能更符合业务发展的需要。架构师需要根据自己的经验和专业知识,综合考虑各种因素,做出最适合当前业务的决策。

最后,需要强调的是,架构设计并不是一项孤立的技术活动,而是需要和业务、运维、开发等各个环节协同配合的。只有将架构设计和整个软件开发、运维流程无缝衔接,才能真正实现架构的价值,为业务的成功提供坚实的支持。

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/426444.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java虚拟机】JVM类加载机制和双亲委派模型

文章目录1.JVM虚拟机类加载子系统2.双亲委派机制和JDK9模块化系统3.ClassLoader源码解读和自定义类加载器场景4.自定义ClassLoader类加载器案例实战5.不同类加载器加载同个class类1.JVM虚拟机类加载子系统 (1)什么是类加载子系统 是Java虚拟机的一个重…

MinIO基础教程

MinIO 1.MinIO安装 Minio 是个基于 Golang 编写的开源对象存储服务,存储非结构化数据,如:图片,视频,音乐等 官网地址:https://min.io/ 中文地址:http://minio.org.cn 官网文档( …

【LeetCode: 300. 最长递增子序列 | 暴力递归=>记忆化搜索=>动态规划】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…

【Vue】收集表单数据 过滤器

收集表单数据 收集表单数据&#xff1a; 若&#xff1a;<input type"text"/>,则v-model收集的是value值&#xff0c;用户输入的就是value值若&#xff1a;<input type"radio"/>,则v-model收集的是value值&#xff0c;且要给标签配置value值若…

树莓派与STM32(rt1064)串口通信

目录 一、树莓派通信 1、硬件连线准备 2、安装Serial和打开树莓派串口 2.1安装Serial 2.2打开树莓派串口 2.3修改串口映射关系 3、树莓派代码 4、上位机 5、运行uart.py代码进行测试 5.1 树莓派发送&#xff0c;上位机接收 5.2上位机发送&#xff0c;树莓派接收 二、…

HopeHomi脚手架(四)redis、redisson模块

项目结构 Redis RedisSpiModuleImport 基于SPI。在项目启动的时候返回待加载类名 public class RedisSpiModuleImport implements SpiEnvironmentModuleImport {Overridepublic String[] readyImportClassName() {return new String[]{RedisConfiguration.class.getName()};…

裸机配置Java环境,解决 -bash: jps: command not found

目录 配置JDK 1、第一步&#xff1a;使用yum命令查找JDK 2、第二步&#xff1a;执行安装命令 3、第三步&#xff1a;验证是否安装成功 4、第四步&#xff1a;验证是否可用 5、第五步&#xff1a;安装开发环境 6、第六步&#xff1a;配置环境变量 今天申请了公司的开发机器&…

【openGauss实战10】备份与恢复

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

视觉SLAM ch12 建图(RGB-D)

一、RGB-D稠密建图 RGB-D相机通结构光和飞行时间获取深度。 稠密重建方法&#xff1a;根据估计的相机位姿&#xff0c;将RGB-D数据转化为点云&#xff0c;然后进行拼接&#xff0c;最终得到由离散的点组成的点云地图。 在此基础上&#xff0c;如果希望估计物体的表面&#x…

Python 实验二 Python语言基础

1.运用输入输出函数编写程序&#xff0c;将华氏温度转换成摄氏温度。换算公式&#xff1a;C(F-32)*5/9,其中 C为摄氏温度&#xff0c;F为华氏温度。 Ffloat(input("请输入你要转换的华氏温度&#xff1a;")) C(F-32)*5/9 print("转换为摄氏温度为&#xff1a;&…

opencv:介绍 SIFT(尺度不变特征变换)及其使用(一)

在本章中 我们将了解 SIFT 算法的概念 我们将学习如何找到 SIFT 关键点和描述符。 理论 在过去的几章中,我们了解了一些角点检测器,如 Harris 等。它们具有旋转不变性,这意味着即使图像旋转,我们也可以找到相同的角点。这是显而易见的,因为旋转后的图像中的角点仍然是角点…

Cairo 1 实战入门:编写测试部署ERC-20代币智能合约

概述 读者可以前往 我的博客 获得更好的阅读体验。 Cairo 是 ZK Rollup 的领域专用语言&#xff0c;目前仅用于 StarkNet 项目。随着 Rollup 叙事的发展&#xff0c;我们认为 cairo 在未来一定会成为智能合约开发的核心语言。 本文类似我之前编写的 Foundry教程&#xff1a;…

BART模型简单介绍

目录 一、概要 二、深入扩展 2.1 预训练任务 2.2 模型精调 一、概要 BART&#xff08;Bidirectional and Auto-Regressive Transformers&#xff09;模型使用标准的基于Transformer的序列到序列结构&#xff0c;主要区别在于用GeLU&#xff08;Gaussian Error Linerar Units…

西安石油大学C++上机实验 上机二:友元和运算符重载程序设计(2学时)

上机二&#xff1a;友元和运算符重载程序设计&#xff08;2学时&#xff09; 实验目的&#xff1a; 了解友元函数的概念。 掌握友元函数的定义和使用方法。 了解运算符重载的概念和使用方法。 掌握几种常用的运算符重载的方法。 实验内容 编写一个时间类&#xff0c;实现…

【面试】记一次中小公司某一次面试题

文章目录1. MySQL中explain执行计划你比较关注哪些字段&#xff1f;2.char、varchar 和 text的区别&#xff1f;3. int(3)和int(11)查询的区别&#xff1f;4. 字段里NULL和空值的区别&#xff1f;5. spring中怎么解决循环依赖问题&#xff1f;5.1 重新设计5.2 使用注解 Lazy5.3…

ESP32设备驱动-HDC2080温湿度传感器驱动

HDC2080温湿度传感器驱动 文章目录 HDC2080温湿度传感器驱动2、硬件准备3、软件准备4、驱动实现HDC2080 器件是一款集成式湿度和温度传感器,可在小型 DFN 封装中以极低的功耗提供高精度测量。基于电容的传感器包括新的集成数字功能和用于消散冷凝和湿气的加热元件。 HDC2080 数…

虹科新闻 | 冲击记录仪ASPION G-Log 2荣获行业领袖奖-Best of Industry Award

虹科ASPION G-Log 2 荣获 行业领袖奖 “2022 Best of Industry Award” 创新的运输冲击数据记录仪虹科ASPION G-Log 2荣获2022年行业领袖奖。经过5个月的投票和近2万张选票&#xff0c;G-Log 2在传感器 由冲击或振动&#xff0c;甚至是温度或相对湿度等气候因素引起的运输损坏…

ERTEC200P-2 PROFINET设备完全开发手册(5-3)

5.3 标识和维护数据&#xff08;I&M&#xff09; 标识和维护数据是一类特殊的数据记录&#xff0c;其中 “I&M0”&#xff08;“16#AFF0”&#xff09;用于有关模块或设备的常规信息。包含的信息例如&#xff1a;订货号/Order-ID, 硬件软件版本/hard- and software v…

强化学习之蒙特卡罗(MC)、动态规划(DP)、时间差分(TD)

强化学习笔记1.马尔可夫决策过程(MDP)1.马尔可夫性质2.马尔可夫过程3.马尔可夫奖励过程(MRP)4.马尔可夫决策过程(MDP)2.蒙特卡罗&#xff08;MC&#xff09;、动态规划&#xff08;DP&#xff09;、时间差分&#xff08;TD&#xff09;1.蒙特卡罗(MC)2.动态规划(DP)3.时间差分(…

【C】Function

系列连载 【python / C / C】 参考 《C语言程序设计&#xff08;第四版&#xff09;谭浩强》【C语言】C语言视频教程《郝斌 C 语言自学教程》 文章目录为什么需要函数什么叫函数如何定义函数函数的分类注意的问题常用的系统函数为什么需要函数 避免重复性操作 有利于程序模…