从第2章到第8章是本书的基础部分(第 1 部分), 要了解架构中的重要权衡,开发人员必须了解有关组件、模块化、耦合和连接性的一些基本概念和术语。
架构师从开发人员的角度看问题,就像气象学家从艺术家的角度看云一样。这称为架构思维。不幸的是,太多的架构师认为架构思维只是“思考架构”。
架构思维远不止于此。它是以架构的眼光或架构的观点来看待事物。像架构师一样思考有四个主要方面。首先,它了解架构和设计之间的区别,并了解如何与开发团队协作以使架构发挥作用。其次,它是在拥有广泛的技术知识的同时仍然保持一定的技术深度,让架构师看到其他人看不到的解决方案和可能性。第三,它是关于理解、分析和协调各种解决方案和技术之间的权衡。最后,它是关于了解业务驱动因素的重要性以及它们如何转化为架构问题。
在本章中,我们将探讨像架构师一样思考和用架构眼光看待事物的这四个方面。
2.1 架构与设计
架构和设计之间的区别常常令人困惑。架构在哪里结束,设计从哪里开始?与开发人员相比,架构师有哪些职责?像架构师一样思考是了解架构和设计之间的区别,并了解两者如何紧密集成以形成业务和技术问题的解决方案。
考虑图 2-1,它说明了架构师与开发人员的传统职责相比。如图所示,架构师负责分析业务需求以提取和定义架构特征(“-ilities”),选择适合问题域的架构模式和样式,以及创建组件(构建模块的构建块)系统)。从这些活动中创建的工件随后被移交给开发团队,该团队负责为每个组件创建类图、创建用户界面屏幕以及开发和测试源代码。
图 2-1 传统的架构与设计观
图 2-1 所示的传统责任模型存在几个问题。事实上,这张图准确地说明了为什么架构很少起作用。具体来说,是单向箭头穿过分隔架构师和开发人员的虚拟和物理屏障,导致了与架构相关的所有问题。架构师做出的决定有时永远不会传达给开发团队,而开发团队做出的更改架构的决定很少会反馈给架构师。在这个模型中,架构师与开发团队脱节,因此架构很少提供它最初打算做的事情。
为了使架构发挥作用,必须打破架构师和开发人员之间存在的物理和虚拟障碍,从而在架构师和开发团队之间形成强大的双向关系。架构师和开发人员必须在同一个虚拟团队中才能完成这项工作,如图 2-2 所示。这种模型不仅促进了架构和开发之间强大的双向沟通,而且还允许架构师为团队中的开发人员提供指导和指导。
图 2-2 通过协作使架构发挥作用
与静态和僵硬的软件架构的老式瀑布方法不同,当今系统的架构在项目的每个迭代或阶段都会发生变化和发展。架构师和开发团队之间的紧密协作对于任何软件项目的成功都是必不可少的。那么架构在哪里结束,设计从哪里开始呢?它没有。它们都是软件项目生命周期的一部分,必须始终保持彼此同步才能取得成功。
2.2 技术广度
技术细节的范围因开发人员和架构师而异。与开发人员必须具有大量的技术深度才能执行其工作不同,软件架构师必须具有大量的技术广度才能像架构师一样思考并从架构的角度看待事物。如图 2-3 所示的知识金字塔说明了这一点,它封装了世界上所有的技术知识。事实证明,技术专家应该重视的信息类型因职业阶段而异。
图 2-3 代表所有知识的金字塔
如图 2-3 所示,任何人都可以将他们所有的知识分为三个部分:你知道的东西,你知道你不知道的东西,你不知道你不知道的东西。
您知道的东西包括技术人员日常用于执行其工作的技术、框架、语言和工具,例如作为 Java 进程员了解 Java。你知道你不知道的东西包括技术专家知道一点或听说过但很少或没有专业知识的东西。这种知识水平的一个很好的例子是 Clojure 编程语言。大多数技术人员都听说过 Clojure,并且知道它是一种基于 Lisp 的编程语言,但他们不会用这种语言编写代码。你不知道你不知道的东西是知识三角的最大部分,包括所有技术、工具、框架和语言,它们将是技术专家试图解决的问题的完美解决方案,但是技术专家甚至不知道这些东西的存在。
开发人员的早期职业专注于扩展金字塔的顶端,以积累经验和专业知识。这是早期的理想重点,因为开发人员需要更多的视角、工作知识和实践经验。扩大顶部顺便扩大中间部分;随着开发人员遇到更多的技术和相关工件,这会增加他们的知识储备。
在图 2-4 中,扩大金字塔的顶端是有益的,因为专业知识受到重视。然而,你知道的东西也是你必须维护的东西——在软件世界中没有什么是静态的。如果开发人员成为 Ruby on Rails 方面的专家,那么如果他们忽略 Ruby on Rails 一两年,这种专业知识就不会持续。金字塔顶端的事情需要时间投入来保持专业知识。最终,个人金字塔顶端的大小就是他们的技术深度。
图 2-4 开发人员必须保持专业知识以留住它
然而,随着开发人员转变为架构师角色,知识的性质发生了变化。架构师的很大一部分价值在于对技术以及如何使用它来解决特定问题的广泛理解。例如,作为一名架构师,知道针对特定问题存在五种解决方案比只拥有一种专业知识更有益。对架构师而言,金字塔最重要的部分是顶部和中部;中间部分深入到底部部分的深度代表了架构师的技术广度,如图2-5所示。
图 2-5 有人知道的是技术深度,有人知道多少是技术广度
作为一名架构师,广度比深度更重要。由于架构师必须做出使功能与技术约束相匹配的决策,因此对各种解决方案的广泛了解很有价值。因此,对于架构师来说,明智的做法是牺牲一些来之不易的专业知识,并利用这段时间来扩大他们的投资组合,如图 2-6 所示。如图所示,一些专业领域将保留下来,可能是在特别令人愉快的技术领域,而另一些则有用地萎缩。
图 2-6 增强架构师角色的广度和缩小深度
我们的知识金字塔说明架构师的角色与开发人员的角色有何根本不同。开发人员的整个职业生涯都在磨练专业知识,而过渡到架构师角色意味着这种观点的转变,许多人觉得这很困难。这反过来又会导致两种常见的功能障碍:首先,架构师试图在广泛的领域保持专业知识,但在任何一个领域都没有取得成功,而且在这个过程中他们自己也衣衫褴褛。其次,它表现为陈旧的专业知识——你过时的信息仍然是前沿的错误感觉。我们经常在大型公司中看到这种情况,在这些公司中,创建公司的开发人员已经担任领导角色,但仍然使用古老的标准做出技术决策(参见第 30 页的“冰雪奇缘穴居人反模式”)。
架构师应该专注于技术广度,这样他们就有更大的箭袋来射箭。过渡到架构师角色的开发人员可能不得不改变他们看待知识获取的方式。在深度和广度方面平衡他们的知识组合是每个开发人员在其整个职业生涯中都应该考虑的事情。
冷冻穴居人反模式
一种在野外常见的行为反模式,即冰冻穴居人反模式,描述了一个架构师,他总是对每个架构都恢复到他们最喜欢的非理性关注。例如,Neal 的一位同事开发了一个以集中式架构为特色的系统。然而,每次他们将设计交付给客户架构师时,始终存在的问题是“但是如果我们失去意大利怎么办?”几年前,一个异常的通信问题导致总部无法与其在意大利的商店进行通信,造成了极大的不便。虽然再次发生的可能性极小,但架构师们对这一特殊的架构特征非常着迷。
通常,这种反模式体现在过去因错误的决定或意外事件而焦头烂额的架构师身上,使他们在未来特别谨慎。虽然风险评估很重要,但也应该切合实际。了解真正的技术风险与感知的技术风险之间的区别是架构师持续学习过程的一部分。像架构师一样思考需要克服这些“冰冻穴居人”的想法和经验,看到其他解决方案,并提出更多相关问题。
2.3 分析权衡
像架构师一样思考就是要在每个解决方案(技术或其他方面)中权衡取舍,并分析这些权衡取舍以确定最佳解决方案。引用马克(您的一位作者)的话:
架构是你无法谷歌的东西。
架构中的一切都是一种权衡,这就是为什么宇宙中每个架构问题的著名答案都是“取决于”。虽然许多人对这个答案越来越恼火,但不幸的是,这是事实。你无法通过谷歌搜索 REST 或消息传递哪个更好,或者微服务是否是正确的架构风格的答案,因为它确实取决于。这取决于部署环境、业务驱动因素、公司文化、预算、时间框架、开发人员技能以及许多其他因素。每个人的环境、情况、问题都不一样,所以架构才这么难。引用 Neal(您的另一位作者)的话:
架构中没有正确或错误的答案——只有取舍。
例如,考虑一个如图 2-7 所示的物品拍卖系统,其中有人为一件物品出价进行拍卖。
图 2-7 拍卖系统取舍的例子——队列还是话题?
Bid Producer 服务生成来自投标人的投标,然后将该投标金额发送到 Bid Capture、Bid Tracking 和 Bid Analytics 服务。这可以通过在点对点消息传递方式中使用队列或通过在发布和订阅消息传递方式中使用主题来完成。架构师应该使用哪一个?你不能用谷歌搜索答案。架构思维要求架构师分析与每个选项相关的权衡,并根据具体情况选择最佳选项。
物品拍卖系统的两个消息选项如图 2-8 和 2-9 所示,图 2-8 说明了主题在发布和订阅消息模型中的使用,图 2-9 说明了使用点对点消息传递模型中的队列。
图 2-8 使用主题在服务之间进行通信
图 2-9 使用队列在服务之间进行通信
图 2-8 中这个问题的明显优势(和看似显而易见的解决方案)是体系结构的可扩展性。 Bid Producer 服务只需要一个主题连接,不像图 2-9 中的队列解决方案,Bid Producer 需要连接到三个不同的队列。如果由于需要向每个投标人提供他们在每次拍卖中所做的所有投标的历史记录而将一项称为投标历史记录的新服务添加到该系统,则根本不需要对现有系统进行任何更改。当创建新的 Bid History 服务时,它可以简单地订阅已经包含投标信息的主题。然而,在图 2-9 所示的队列选项中,Bid History 服务需要一个新队列,并且需要修改 Bid Producer 以向新队列添加额外的连接。这里的要点是,使用队列需要在添加新的出价功能时对系统进行重大更改,而使用主题方法则不需要对现有基础架构进行任何更改。另外,请注意 Bid Producer 在主题选项中更加解耦——Bid Producer 不知道投标信息将如何使用或由哪些服务使用。在队列选项中,出价制作者确切地知道出价信息是如何使用的(以及由谁使用),因此与系统的耦合度更高。
通过此分析,使用发布和订阅消息传递模型的主题方法似乎是显而易见的最佳选择。然而,引用 Clojure 编程语言的创造者 Rich Hickey 的话:
程序员知道什么有好处但不会权衡利弊,架构师需要两者都要了解。
从架构上思考是在为了发现给定解决方案的好处,但也要分析与解决方案相关的负面影响或权衡取舍。继续以拍卖系统为例,软件架构师将分析主题解决方案的负面影响。在分析差异时,首先注意到图 2-8 中,对于一个主题,任何人都可以访问投标数据,这可能会引入数据访问和数据安全问题。在图 2-9 所示的队列模型中,发送到队列的数据只能由接收该消息的特定消费者访问。如果流氓服务确实在队列中监听,相应的服务将不会收到这些出价,并且会立即发送有关数据丢失的通知(因此可能存在安全漏洞)。换句话说,很容易窃听一个主题,而不是一个队列。
除了安全问题,图 2-8 中的主题解决方案仅支持同构合约。所有接收投标数据的服务必须接受相同的合同和投标数据集。在图 2-9 中的队列选项中,每个消费者都可以有自己的合同,具体到它需要的数据。例如,假设新的出价历史服务需要当前要价和出价,但没有其他服务需要该信息。在这种情况下,合同将需要修改,从而影响使用该数据的所有其他服务。在队列模型中,这将是一个单独的信道,因此是一个不影响任何其他服务的单独合同。
图 2-8 所示的主题模型的另一个缺点是它不支持监控主题中消息的数量,因此不支持自动缩放功能。但是,使用图 2-9 中的队列选项,可以单独监控每个队列,并将进程化负载平衡应用于每个出价消费者,以便每个消费者都可以自动缩放,相互独立。请注意,这种权衡是特定于技术的,因为高级消息队列协议(AMQP)可以支持编程负载平衡和监控,因为交换(生产者发送的内容)和队列(消费者收听的内容)之间的分离到)。
鉴于这种权衡分析,现在哪个是更好的选择?答案呢?这取决于具体情况!表 2-1 总结了这些权衡。
表 2-1 主题的权衡
主题优势 |
主题缺点 |
体系结构扩展性 |
数据访问和数据安全问题 |
服务解耦 |
无异构合约,监视和编程可伸缩性 |
这里的重点是,软件架构中的所有内容都有一个权衡:优点和缺点。像架构师一样思考是分析这些权衡,然后问“哪个更重要:可扩展性还是安全性?不同解决方案之间的决策将始终取决于业务驱动因素、环境和许多其他因素。
2.4 了解业务驱动因素
像架构师一样思考是了解系统成功所需的业务驱动因素,并将这些需求转化为体系结构特征(如可伸缩性、性能和可用性)。这是一项具有挑战性的任务,要求架构师具有一定程度的业务领域知识,并与关键业务利益干系人创建健康的协作关系。我们在书中专门用了好几章来讨论这个特定的主题。在第4章中,我们定义了各种架构特征。在第5章中,我们描述了识别和鉴定架构特征的方法。在第6章中,我们将介绍如何测量这些特征中的每一个,以确保满足系统的业务需求。
2.5 平衡架构和动手编码
架构师面临的一项艰巨任务是如何平衡动手编码与软件架构。我们坚信每个架构师都应该编码并能够保持一定水平的技术深度(参见第 25 页的“技术广度”)。虽然这似乎是一项容易的任务,但有时很难完成。
在动手编码和成为软件架构师之间寻求平衡的第一个技巧是避免瓶颈陷阱。当架构师在项目的关键路径(通常是底层框架代码)中获得代码所有权并成为团队的瓶颈时,就会出现瓶颈陷阱。发生这种情况是因为架构师不是全职开发人员,因此必须在扮演开发人员角色(编写和测试源代码)和架构师角色(绘制图表、参加会议,以及参加更多会议)之间取得平衡。
作为一名有效的软件架构师,避免瓶颈陷阱的一种方法是将关键路径和框架代码委托给开发团队中的其他人,然后专注于编写业务功能(服务或屏幕)的一到三个迭代路。这样做会发生三件积极的事情。首先,架构师正在获得编写生产代码的实践经验,同时不再成为团队的瓶颈。其次,关键路径和框架代码被分发给开发团队(它所属的地方),让他们拥有所有权并更好地理解系统中较难的部分。第三,也许是最重要的一点,架构师正在编写与开发团队相同的业务相关源代码,因此能够更好地了解开发团队在流程、过程和开发环境。
然而,假设架构师无法与开发团队一起开发代码。软件架构师如何才能保持亲身实践并保持一定程度的技术深度?架构师可以通过四种基本方式在工作中保持亲力亲为,而不必“在家练习编码”(尽管我们也建议在家练习编码)。
第一种方法是经常进行概念验证或 POC。这种做法不仅需要架构师编写源代码,而且还有助于通过考虑实现细节来验证架构决策。例如,如果架构师无法在两种缓存解决方案之间做出决定,帮助做出此决定的一种有效方法是在每个缓存产品中开发一个工作示例并比较结果。这使架构师能够亲眼看到实施细节和开发完整解决方案所需的工作量。它还允许架构师更好地比较架构特征,例如不同缓存解决方案的可伸缩性、性能或整体容错能力。
我们在进行概念验证工作时的建议是,只要有可能,架构师就应该尽可能地编写具有最佳生产质量的代码。我们推荐这种做法有两个原因。首先,一次性的概念验证代码经常进入源代码存储库,并成为其他人可以遵循的参考架构或指导示例。架构师最不希望的是他们的一次性、草率代码成为他们典型工作的代表。第二个原因是,通过编写生产质量的概念验证代码,架构师可以练习编写高质量、结构良好的代码,而不是不断地开发糟糕的编码实践。
架构师可以保持亲力亲为的另一种方式是解决一些技术债务故事或架构故事,让开发团队腾出时间来处理关键的功能用户故事。这些故事通常是低优先级的,所以如果架构师没有机会在给定的迭代中完成技术债务或架构故事,那不是世界末日,通常不会影响迭代的成功。
同样,在迭代中修复错误是保持动手编码的另一种方式,同时也有助于开发团队。虽然肯定不是迷人的,但这种技术允许架构师识别代码库和架构中可能存在的问题和弱点。
通过创建简单的命令行工具和分析器来利用自动化来帮助开发团队完成日常任务,这是保持动手编码技能同时提高开发团队效率的另一种好方法。寻找开发团队执行的重复任务并使流程自动化。开发团队将感谢自动化。一些示例是自动源代码验证器,以帮助检查在其他 lint 测试、自动检查列表和重复的手动代码重构任务中找不到的特定编码标准。
自动化还可以以架构分析和适应度功能的形式来保证架构的活力和合规性。例如,架构师可以在 Java 平台的 ArchUnit 中编写 Java 代码来自动化架构合规性,或者编写自定义适应度函数来确保架构合规性,同时获得实践经验。我们将在第 6 章中讨论这些技术。
作为架构师要保持亲力亲为的最后一个技巧是经常进行代码审查。虽然架构师实际上并没有编写代码,但至少他们参与了源代码。此外,进行代码审查还有一个额外的好处,即能够确保符合架构并在团队中寻求指导和辅导机会。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库