软件架构基础——识别架构特性

117 阅读24分钟

为了创建架构或确定现有架构的有效性,架构师必须分析两个方面:架构特性和领域。如第4章所述,识别一个问题或应用程序所需的正确架构特性(“-ility”)不仅需要理解领域问题,还需要与利益相关者合作,确定从领域角度来看什么是最重要的。

项目需要的架构特性至少有三个来源:领域关注、项目需求和你隐性的领域知识。除了像安全性和模块化这样的通用隐性架构特性外,我们还注意到一些领域还包含隐性架构特性。例如,一个从事医学软件开发的架构师,负责从诊断设备中读取数据,应该已经理解数据完整性的重要性以及丢失信息可能带来的后果。专注于该领域的架构师会内化数据完整性,因此它会在他们设计的每一个解决方案中成为隐性要求。

从领域关注中提取架构特性

大多数架构特性来自于倾听关键领域利益相关者的意见,并与他们合作,确定从业务角度来看什么是重要的。尽管这似乎很直接,但问题在于架构师和领域利益相关者讲的是不同的语言。架构师谈论可扩展性、互操作性、容错性、可学习性和可用性;领域利益相关者谈论并购、用户满意度、市场上市时间和竞争优势。结果就是一种“失之翻译”的问题,架构师和领域利益相关者彼此不理解。架构师不知道如何创建支持用户满意度的架构,而领域利益相关者不理解为什么架构师如此注重应用程序的可用性、互操作性、可学习性和容错性。

幸运的是,可以将领域关注转化为架构特性语言。表5-1展示了一些常见领域关注及其对应的“-ility”特性。理解关键的领域目标可以帮助架构师将这些领域关注转化为“-ility”,从而为正确且有理有据的架构决策奠定基础。例如,市场上市时间的领域关注是否比可扩展性更重要,或者架构师应该专注于容错性、安全性或性能?也许该系统需要这些特性组合在一起。

表5-1:将领域关注转化为架构特性

领域关注架构特性
并购互操作性、可扩展性、适应性、可扩展性
市场上市时间敏捷性、可测试性、可部署性
用户满意度性能、可用性、容错性、可测试性、可部署性、敏捷性、安全性
竞争优势敏捷性、可测试性、可部署性、可扩展性、可用性、容错性
时间和预算简单性、可行性

复合架构特性

在将领域关注转化为架构特性时,一个常见的陷阱是错误地将一些概念等同起来,比如仅将敏捷性等同于市场上市时间。敏捷性是一个复合架构特性:它没有单一的客观定义,而是由其他可衡量的内容组成。敏捷性没有单一的衡量标准,因此架构师必须问:敏捷性由哪些方面组成?它包括可部署性、模块化、可测试性等,这些都是可以衡量的。

一个常见的反模式是架构师过于狭隘地关注复合特性中的某一部分,通常是为了方便。就像忘记把面粉放入蛋糕面糊中一样。例如,领域利益相关者可能会说:“由于监管要求,必须按时完成每日基金定价。”

一个无效的架构师可能会回应,仅关注性能,因为这似乎是领域关注的主要焦点。然而,这种做法将失败,原因有很多:

  • 如果系统在需要时无法使用,那么系统的速度再快也没用。
  • 随着领域的扩展和更多基金的创建,系统必须能够扩展以在规定时间内完成日终处理。
  • 系统不仅必须可用,还必须可靠,以确保在进行日终基金定价时不会崩溃。
  • 如果系统在完成85%的日终基金定价时崩溃怎么办?它必须能够恢复并从崩溃位置重新开始定价。
  • 最后,系统虽然很快,但计算出的基金价格正确吗?

因此,除了性能外,这位架构师必须同样关注可用性、可扩展性、可靠性、可恢复性和可审计性。

许多商业目标最初是复合架构特性。将它们分解并为结果特性提供客观定义是架构师的工作之一。(我们将在第6章中看到这一点的重要性。)

提取架构特性

大多数架构特性来自于某种形式的需求文档中的明确陈述。例如,领域关注点的列表通常包括预期用户数量和规模等明确内容。其他特性则来自于架构师的固有领域知识(这是每个架构师都应该掌握自己领域的原因之一)。

例如,假设你是一个架构师,设计一个处理大学生选课的应用程序。为了简化数学计算,假设学校有1,000名学生,并且选课时间为10小时。你是否应该假设学生会在这10小时内平均分布,设计一个可以处理稳定规模的系统?还是根据你对大学生习惯和倾向的了解,应该设计一个能够处理所有1,000名学生在最后10分钟内同时注册的系统?

任何了解学生典型拖延行为的人都知道这个问题的答案!类似的细节很少出现在需求文档中——然而,它们会影响设计决策。

架构 Kata 的起源

十多年前,著名架构师 Ted Neward 提出了架构 Kata,这是一个巧妙的方法,可以让新晋架构师练习如何从面向领域的描述中推导出架构特性。“Kata”一词源自日语,指的是一种武术训练方法,强调正确的形式和技巧。

我们如何培养出优秀的设计师?当然,优秀的设计师通过设计来成长。

——Fred Brooks
大型架构项目需要时间,架构师的职业生涯中通常设计不到十个系统。那么,我们应该如何培养出优秀的架构师呢?关键是给有潜力的架构师提供实践的机会。为了提供一个课程体系,Ted 创建了第一个架构 Kata 网站,书籍的作者 Neal 和 Mark 在《软件架构基础》网站(fundamentalsofsoftwarearchitecture.com)上对其进行了改进和更新。忠于原始目的,架构 Kata 为有志成为架构师的人提供了一个有用的实验室。

与 Kata 一起工作

基本的前提是,每个 Kata 练习提供一个问题,用领域术语表述,包含一组需求和一些额外的上下文(这些可能在需求中没有出现,但却可能影响设计)。小团队在规定的时间内完成设计(包括架构特性分析和图表)。然后,团队分享他们的结果,并投票选出谁提出了最佳架构。

每个 Kata 都有预定义的部分:

描述
系统试图解决的整体领域问题。

用户
系统的预期用户数量和/或类型。

需求
来自领域用户和专家的领域需求。

额外上下文
作者在之前提到的网站上更新了 Kata 格式,增加了一个“额外上下文”部分,涵盖了重要的考虑事项,使练习更加现实。

我们鼓励读者利用该网站进行自己的 Kata 练习。任何人都可以举办午餐会,邀请一个有潜力的架构师团队来解决问题。一位有经验的架构师可以在现场或事后评估设计和权衡分析,讨论遗漏的权衡和备选设计。由于练习有时间限制,因此设计不会过于复杂。

接下来,我们将通过一个架构 Kata 来演示架构师如何从需求中提取架构特性。欢迎进入 Silicon Sandwiches Kata。

Kata:硅沙拉三明治

描述
一家全国性的三明治店希望在现有的电话订单服务之外,启用在线订购功能。

用户
成千上万,或许有一天是数百万。

需求
允许用户下订单,如果商店提供送货服务,可以选择自取或送货。
给自取客户提供取餐时间和到店路线(该功能必须与多个外部地图服务集成,包括交通信息)。
对于送货服务,将司机与订单一起派送到用户处。
提供移动设备访问功能。
提供全国性的每日促销和特价。
提供本地每日促销和特价。
接受在线支付、店内支付或送货时支付。

额外上下文
三明治店为特许经营,每家店有不同的老板。
母公司有计划在不久的将来扩展到海外。
公司的目标是雇佣廉价劳动力以最大化利润。

根据这个情境,你将如何推导出架构特性?需求的每个部分可能会影响架构的一个或多个方面(很多部分可能不会)。架构师在这里不设计整个系统——仍然需要大量的努力来编写代码解决领域设计问题(第8章将详细讨论)。相反,应该关注那些影响或影响设计的事项,尤其是结构性方面。

首先,将候选架构特性分为显式特性和隐式特性。

显式特性

显式架构特性出现在需求规范中,作为必要设计的一部分。例如,一个购物网站可能期望支持特定数量的并发用户,域分析师会在需求中进行说明。考虑需求的每个部分,看看它是否有助于架构特性。但首先,考虑有关预期指标的领域级预测,如Kata中的“用户”部分所表示的。

首先吸引你注意的细节之一是用户数量:目前是成千上万,也许有一天是数百万(这是一个非常有雄心的三明治店!)。因此,可伸缩性——即在没有严重性能下降的情况下处理大量并发用户的能力——是最重要的架构特性之一。注意,问题陈述没有明确要求可伸缩性,而是通过预期的用户数量表达了这一需求。架构师必须经常将领域语言解码为工程等价物。

你可能还需要弹性——即处理请求突发的能力。这两个特性通常被归为一类,但它们有不同的约束条件。可伸缩性就像图5-1所示的图表。

image.png

另一方面,弹性衡量的是流量的突发情况,如图5-2所示。

image.png

有些系统是可扩展的,但不是弹性的。例如,一个酒店预订系统的用户数量,在没有特殊促销或事件的情况下,可能是季节性可预测的。相反,考虑一个演唱会门票预定系统。当新票开始发售时,狂热的粉丝将涌入网站,这需要较高的弹性。通常,弹性系统也需要可扩展性:即能够处理流量突发和大量并发用户的能力。

弹性在“硅制三明治”需求中并没有明确提到,但你仍然应该将其视为一个重要的考虑因素。需求有时会明确指出建筑特性,而其他特性则隐藏在问题领域中。三明治店的流量是全天均匀的,还是在餐时段有流量高峰?几乎可以肯定是后者,因此要识别这个潜在的建筑特性。

接下来,逐一考虑以下业务需求,看看它们是否需要特定的建筑特性:

  1. 用户下订单,并且如果店铺提供送餐服务,可以选择自取或送餐。

    • 这个需求似乎不需要任何特别的建筑特性支持。
  2. 自取的顾客会被告知取餐时间和店铺位置(且必须集成多个外部地图服务,包括交通信息)。

    • 外部地图服务意味着集成点,可能会影响诸如可靠性等方面。例如,假设开发者构建的系统依赖于第三方系统。如果这些调用失败,将会影响调用系统的可靠性。另一方面,要小心不要过度指定建筑特性。如果外部交通服务出现故障,硅制三明治网站是应该完全失败,还是应该在没有交通信息的情况下稍微降低效率?架构师应该时刻防止在设计中增加不必要的脆弱性或易损性。
  3. 提供送餐服务,将司机和订单一起派送给用户。

    • 这个需求似乎不需要特别的建筑特性支持。
  4. 提供移动设备访问。

    • 这个需求主要会影响应用程序的用户体验(UX)设计,指向构建一个便携式Web应用程序或多个原生Web应用程序。鉴于预算限制和应用程序的简单性,构建多个应用程序可能会有些过度,因此设计指向一个移动优化的Web应用程序。因此,可能需要定义一些特定的性能相关建筑特性,以优化页面加载时间和其他与移动相关的特性。

在这种情况下,你不应单打独斗。与UX设计师、领域利益相关者和其他相关方合作,以验证这样的决策。例如,业务可能需要一些行为,这些行为只有通过构建原生应用程序才能实现。

  1. 提供全国每日促销和特价;提供本地每日促销和特价。

    • 这两个需求都指定了跨促销和特价的定制化。需求1还暗示了根据用户地址定制交通信息。根据这两个需求,你可以考虑定制性作为一个建筑特性。例如,微内核架构风格(第13章讨论)通过定义插件架构非常好地支持定制行为。在这种情况下,默认行为出现在核心中,开发人员将基于位置的定制部分作为插件编写。然而,传统设计也可以通过设计模式(例如模板方法)来满足这一需求。这种困境在架构中很常见,需要架构师不断权衡相互竞争的选项。我们在《设计与架构及权衡》中会更详细地讨论具体的权衡。
  2. 接受在线支付、店内支付或送餐时支付。

    • 在线支付意味着需要安全性,但这个需求没有暗示比隐含的更高的安全级别。架构师可以通过设计或架构来处理安全问题,这使得它在这个应用程序中的建筑特性关注点较小。
  3. 三明治店是特许经营,每个店铺有不同的老板。

    • 这个需求可能会对架构施加成本限制。检查可行性,应用诸如成本、时间和员工技能集等约束条件,看看是否需要一个简单或牺牲的架构。
  4. 总公司有计划在近期扩展到海外。

    • 这个需求暗示了国际化(通常简称为“i18n”)。许多设计技术可以处理这个需求,而它通常不需要特别的结构来支持。然而,这肯定会推动用户体验决策。
  5. 企业目标是雇佣廉价劳动力以最大化利润。

    • 这个需求表明可用性将是重要的,但它更多地与设计有关,而不是建筑特性。

从前面的需求中,我们可以衍生出第三个建筑特性:性能:没有人愿意在一个性能差的三明治店购买东西,尤其是在高峰时段。然而,性能是一个复杂的概念。你应该设计什么样的性能?(我们在第6章中讨论了性能的各种细微差别。)

同样,重要的是要将性能数字与可扩展性数字一起定义。换句话说,建立一个没有特定规模的性能基准,并确定在一定数量的用户情况下可接受的性能水平。建筑特性经常相互作用,迫使架构师定义它们之间的关系。

隐式特性

许多架构特性并未在需求文档中明确规定,但它们却是设计的重要组成部分。系统可能需要支持的一个隐式架构特性是可用性:确保用户可以访问网站。与可用性密切相关的是稳定性:确保在交互过程中网站保持运行。没有人愿意从一个不断断开连接的站点购物,这会迫使他们重新登录。

安全性在每个系统中都是隐式特性:没有人想创建不安全的软件。然而,您可能会根据安全性的重要性来为其设定不同的优先级,这也说明了我们定义中相互交织的特性。只要它影响设计的某些结构方面,并且对应用至关重要或重要,就可以将安全性视为一个架构特性。

对于硅三明治系统,您可以假设支付应该由第三方处理。因此,只要开发人员遵循一般的安全规范(例如不以明文传递信用卡号、不存储过多信息等),良好的应用设计就足够了;您无需特别的结构设计来适应安全性。记住,架构特性是相辅相成的——每个架构特性与其他特性相互作用。这也是为什么过度指定架构特性是一个常见的陷阱。过度指定和未指定特性一样具有破坏性,因为它使系统设计变得过于复杂。

硅三明治需要支持的最后一个主要架构特性是可定制性,它由需求中的几个细节组成。问题领域中的多个部分提供了可定制的行为:食谱、本地销售和可能被本地覆盖的路线。因此,架构应支持定制行为。通常,这会属于应用设计的范畴。然而,正如我们的定义所指出的,当问题领域的部分依赖于定制结构时,它就转变为架构特性。这个设计元素对应用的成功并非至关重要。记住,选择架构特性时没有正确的答案,只有错误的答案——或者:

架构没有错误的答案,只有昂贵的答案。

——Mark的名言之一

设计与架构及权衡

在硅三明治的案例中,您可能会将可定制性视为系统的一部分,但问题是:是架构还是设计?架构暗示某种结构性组件,而设计则是在架构之内。对于硅三明治,您可以选择像微内核这样的架构风格,建立结构支持以进行定制。然而,如果因为其他竞争性考虑而选择另一种风格,开发人员也可以使用模板方法设计模式来实现定制,该模式允许父类定义工作流,子类可以重写这些工作流。哪个选项更好?

像架构中的一切一样,这取决于。首先,是否有充分的理由不实现微内核架构(例如性能和耦合性)?其次,其他期望的架构特性在某一设计中是否比在另一设计中更难实现?第三,支持架构中所有架构特性的成本与在设计中支持它们的成本如何?这种类型的权衡分析是架构师角色中的重要部分。

最重要的是,与开发人员、项目经理、运维团队和其他共同构建软件系统的人员合作。任何架构决策都不应脱离实施团队单独做出(这会导致臭名昭著的象牙塔架构反模式)。在硅三明治的案例中,架构师、技术负责人、开发人员和领域分析师应该合作决定如何最佳地实现可定制性。

在这种情况下,您不必过于担心发现完全正确的架构特性集合。开发人员可以通过多种方式实现功能。然而,正确识别出重要的结构性元素可能会帮助您设计出更简单或更优雅的方案。您可以设计一个不在结构上支持可定制性的架构,而是要求应用程序设计本身支持这种行为(请参见“设计与架构及权衡”)。记住:架构中没有最好的设计,只有一系列最小坏的权衡集合。

在试图找到最简单所需的架构特性集合时,应优先考虑这些架构特性。一旦团队完成了架构特性的初步识别,一个有用的练习是尝试确定最不重要的特性。如果您必须去除一个,它会是哪一个?通常,架构师更倾向于剔除显式的架构特性,因为许多隐式特性支持系统的总体成功。我们定义什么对成功至关重要或重要,帮助确定应用是否真正需要每个架构特性。尝试确定最不适用的特性可以帮助您确定真正的必要性。

对于硅三明治系统,我们已识别的架构特性中,哪个最不重要?同样,无法给出绝对正确的答案。然而,在这种情况下,解决方案可以放弃可定制性或性能。我们可以将可定制性从架构特性中剔除,计划将这种行为作为应用设计的一部分来实现。在操作架构特性中,性能可能是对成功最不重要的因素。当然,这并不意味着开发人员打算构建一个性能极差的应用;这仅仅意味着该设计并没有优先考虑性能,而是将其他特性(如可扩展性或可用性)放在首位。

限制和优先排序架构特性

与领域利益相关者合作定义主导架构特性时,有一个小贴士:尽量保持最终列表尽可能简短。一个常见的反模式是尝试设计一个通用架构:支持所有架构特性的架构。每个架构特性都会使整体系统设计更加复杂,因此支持过多的架构特性会导致越来越复杂的系统设计。而这一切还在架构师和开发人员开始处理问题领域之前——即编写软件的初衷。不要过分关注特性的数量,而是要记住动机:保持设计简单。

案例研究:瓦萨号

关于架构特性过度指定到最终导致项目失败的原始故事,瓦萨号无疑是其中的经典案例。瓦萨号是一艘瑞典战舰,于1626年至1628年间建造,由一位希望拥有最宏伟战舰的国王下令建造。在此之前,船只通常分为军队运输船和战舰,而瓦萨号则是两者兼具。大多数船只只有一层甲板,而瓦萨号有两层!所有的炮口比类似船只的两倍大。尽管专家们有些犹豫,船匠们还是无法拒绝阿道夫国王的命令。

为了庆祝建造完成,瓦萨号驶出斯德哥尔摩港,并在一侧鸣响了礼炮。不幸的是,由于船体重心偏高,瓦萨号翻覆并沉入了海底。1961年,打捞人员成功救起了这艘船,目前它陈列在斯德哥尔摩的一座博物馆内。

在进行架构特性分析时,架构师和业务利益相关者的合作至关重要。然而,如果架构师提供了一个可能的架构特性列表,并要求业务利益相关者选择他们希望架构支持的特性,每次答案都会是什么呢?“全部!”

因此,架构师需要一种方法来确定哪些架构特性推动了结构决策,并且对成功至关重要。作者们开发了许多方法来促进这一努力,包括架构特性“工作表”,如图5-3所示(可以从developertoarchitect.com/resources.h…下载)。

该工作表用于架构师主持的互动会议中,架构师征求利益相关者对所需架构特性的数量和细节的意见。在左侧,可以列出所需架构特性的七个槽位。

为什么是七个?为什么不呢?说实话,架构师需要将列表限制在一个合理的数量范围内——六个或八个也可以(尽管七这个数字背后有一些有趣的心理学)。第二列包括一些隐式的架构特性;这些特性出现在大多数系统中,但有时架构师会将它们作为应用程序的主导关注点,认为它们需要特殊的设计和考虑。在这些情况下,架构师将隐式特性“拉入”第一列。如果第一列已经满了,并且出现了更好的特性来替代现有的某个特性,架构师会将它移到“考虑过的其他”类别中。

image.png

最后一步是通过合作选择三个最优先的架构特性,顺序不限(在每个特性旁边打勾)。这个练习可以帮助架构师从中提炼出一个简化的、按优先级排序的驱动因素列表,架构师可以利用这些因素来推动设计决策和权衡分析。

许多架构师和领域利益相关者希望优先确定应用程序或系统必须支持的架构特性最终列表。虽然这无疑是一个理想的结果,但在大多数情况下,这是一项徒劳的任务,不仅会浪费时间,还会产生许多不必要的挫折和与关键利益相关者的争执。很少会有所有利益相关者对每一个特性的优先级达成一致。更好的方法是让领域利益相关者从最终列表中选择三个最重要的特性(顺序不限)。这样可以更容易达成共识,并促进关于什么最重要的讨论。所有这些都有助于架构师在做出关键架构决策时分析权衡。