软件架构设计:实用方法及实践
上QQ阅读APP看书,第一时间看更新

2.4 架构驱动因子

在开始使用ADD(对于任何其他的设计方法也是同样的)设计之前,你需要考虑你在做什么,为什么要做。虽然这句话似乎是显而易见的,但是像往常一样,麻烦在细节中。我们将这些“什么”和“为什么”的问题归类为软件架构驱动因子。如图2.1所示,这些驱动因子包括一个设计目的、质量属性、主要功能、架构关注点和约束条件。这些因素是系统成功的关键,因此,它们驱动并使软件架构成形。

与任何其他重要的需求一样,在整个开发的生命周期中,软件架构驱动因子需要一个基准并需要被管理。

2.4.1 设计目的

首先,你需要明确你想要实现的设计目的。什么时候和为什么你要做这个软件架构设计?在这个时候机构最关心哪个业务目标?

1.你可能正在把软件架构设计作为项目提案的一部分执行(正如我们在9.1.1节中讨论的,为了咨询机构的售前过程或者公司的内部项目选择和优先级确定)。创建一个初始的软件架构来作为确定部分项目可行性、进度和预算并不少见。这样的架构不会非常详细,它的目的是了解架构并将其分解为足够详细。这样可以让我们理解工作单元,并可以做出评估。

2.你可能正在把软件架构设计作为创建一个探索原型过程的一部分来执行。在这种情况下,软件架构设计过程的目的不是要创建一个可发布或可重用的系统,而是探索领域,探索新的技术,给客户提供一些可执行程序来得到快速的反馈,或发现一些质量属性(如性能、可扩展性和为了提高可用性的故障转移)。

3.你可能正在开发过程中设计你的架构。这可能是一个全新的系统、作为新系统的一个补充部分或作为现有的系统被重构或替换的一部分。在这种情况下,架构设计的目的是要用足够多的设计工作来满足需求,指导系统的建设和工作分配,并准备最终的发布。

这些目的在成熟领域的绿地(greenf ield)系统绿地系统,英文原文是greenfield system,指在少量代码或没有遗留代码的基础上开发的系统。——译者注、新兴领域的绿地系统和现有系统中可以得到不同的解释和实现。在一个成熟的领域,如售前过程,可能比较简单;软件架构师可以重用现有的系统作为样例,基于类比做出估计。在新兴领域,售前评估的过程将更为复杂且有风险,并且可能会有很多种结果。在这些情况下,可能需要创建一个原型系统或系统的一个关键部分来减轻风险和减少不确定性。在许多案例中,当我们获知或者接收新的需求时,也可能需要很快地改变这个架构。在棕地系统棕地系统,英文原文是brownfield system,指在遗留系统中进行的开发。——译者注 中,虽然需求可以被更好地理解,但是现有的系统本身就是一个复杂的对象,我们必须充分了解它我们的计划才会准确。

最后,开发组织在开发或维护过程中的目标可能会影响到架构设计过程。例如,组织可能对为复用设计、为将来的扩展或裁剪设计、为可扩展性设计、为持续发布设计、为最好地利用现有的项目能力和团队成员的技能设计等感兴趣。或组织可能与供应商有战略合作关系。或CIO可能有特定的喜好或者厌恶,并想把它强加给你的项目。

为什么我们要费心列出这些考虑因素呢?因为它们会影响设计的过程和设计的产出。架构的存在是为了帮助实现业务目标。软件架构师应该清楚这些目标,沟通这些目标(和交涉这些目标!)并在设计过程开始前建立一个清晰的设计目标。

2.4.2 质量属性

在《Software Architecture in Practice》这本书中,质量属性被定义为一个系统的可测量的或可测试的属性,用来表示系统是如何很好地满足其利益相关者的需求。因为质量本身往往是一个主观的概念,这些属性使我们可以简洁和客观地表达质量。

在驱动因子中,质量属性是最能使架构成形的。当你设计软件架构时做出的关键选择将在很大程度上决定你的系统能不能满足这些驱动质量属性的目标。

鉴于其重要性,你必须要考虑捕获、确定质量属性、优先级化并验证质量属性。考虑到这么多事情依赖于驱动因子的正确性,这听起来像是一项艰巨的任务。幸运的是,一些很好理解并且广泛传播的技术可以在这里帮助你(参看下面的引文“质量属性研讨会和效用树”):

❑ 质量属性研讨会(QAW)是一个简化的头脑风暴会议,它涉及一组系统的干系人,包括质量属性的捕获、确定、优先级化、达到一致等活动。

❑ 任务线程研讨会的目的与QAW相同,区别只是针对系统中的一个系统。

❑ 效用树可以被软件架构师用来根据质量属性的技术难度和风险考虑它们的需求的优先级。

我们认为最好对质量属性的需求做讨论、记录,指定优先级的方式是把它们作为一组场景。一个场景以其最基本的形式描述了系统对一些刺激因素的反应。为什么多个场景是最好的方法?因为所有其他的方法都更糟!无穷无尽的时间可能被浪费在定义诸如“性能”或“可修改性”或“可配置性”上,而这些争论往往对真实系统没有什么意义。一个系统是“可修改的”这种说法毫无意义,因为每一个系统对另外的系统来说都有可修改的方面和不可修改的方面。但是,在对一个特定的变更请求的响应上,我们可以指定我们想要达到的可修改性的度量(如时间或人力)。例如,您可能希望指定“在电子商务上更新运输费可以用1个人天完成并测试完毕”,这是一个明确的标准。

因此质量属性场景的核心是一对刺激与响应。假设你正在做一个视频游戏,你有这样的功能需求:“当用户按下‘C’键时游戏将改变视角”。如果这个功能的需求是重要的,它就需要关联到质量属性需求。例如:

❑ 这个功能需要有多快。

❑ 这个功能需要有多安全。

❑ 这个功能需要多大的修改自由度。

为了解决这个问题,我们使用一个场景来描述一个质量属性的需求。一个质量属性的场景是对一个系统如何应对一些刺激的一个简短的描述。例如,我们可能会对前面给出的功能性需求进行如下注释:“当用户按下‘C’键时,游戏将在500毫秒内改变视角”。一个场景把一个刺激(在这里是按下“C”键)和响应(改变视角)使用一个响应的度量(500毫秒)结合在一起。一个完整的质量属性场景还包含其他三部分:刺激源(这里是用户)、影响的工件(在这里因为我们处理的是端到端的延迟,工件是整个系统)和环境(我们是在正常运行、启动、降级模式或者一些其他方式)。总的说来,一个定义良好的场景有6个部分,如图2.2所示。

图2.2 质量属性场景的6个组成部分

场景是经过深思熟虑的、关于系统中的质量属性行为的、可测试、可检验的假设。因为它们有明确的刺激和响应,所以我们可以根据设计对场景的支持程度来评估它。我们还可以在实践中测量和测试原型或是完全更新了的系统是否满足该场景。如果分析(或原型的结果)表明不能达到场景的响应目标,那么假设被证明并不成立。

与其他需求一样,场景应该划分优先级。这可以通过权衡与每个场景相关联的两个维度并赋予一个重要性等级来实现:

❑ 第一个维度对应于该场景对于系统成功的重要性。这是由客户做出的排序。

❑ 第二个维度对应于与该场景相关的技术风险程度。这是由软件架构师做出的排序。

一个低/中/高(L/M/H)的级别被用来排名这两个维度。一旦这两个维度已经被排名,就可以通过选择那些有(H, H)(H, M)或(M, H)排名组合的场景来划分场景的优先级。

此外,一些传统需求建模技术可以稍作修改来关注质量属性的需求,如联合需求计划(JRP)、联合应用程序设计(JAD)、原型化分析和加速系统分析法。

但无论你使用什么技术,不要在还没有做好可测量的质量属性的优先列表之前开始做设计!虽然利益相关者可能会以没有思路为理由(“我不知道它需要有多快,只要让它快就行!”),但是你几乎总能至少引出一个可能的响应范围。要与利益相关者确定一个10秒的响应时间是否可以接受而不是说该系统应该是“快的”。如果这是不可接受的,继续询问5秒或者1秒是不是可以。你会发现,在大多数情况下,用户对他们的需求知道的比他们意识到的更多,你至少可以把它们“框”到一个范围。

质量属性研讨会和效用树

质量属性研讨会

QAW是一个便利的,以利益相关者为中心的方法,用来生成、划分优先级和细化质量属性场景。虽然在实践中,我们已经看到QAW被用于软件开发生命周期中的所有方面,但是理想状况下一个QAW会议应该在软件架构已定义之前召开。QAW集中在系统级关注点和软件会在系统中所起的具体作用上。QAW的步骤如下:

1. QAW演示和介绍

QAW的主持人描述QAW的动机并解释这个方法的步骤。

2.业务目标陈述

一个代表项目的业务关注点的利益相关者介绍系统的业务背景、宽泛的功能需求、约束条件和已知的质量属性需求。QAW靠后的步骤中细化的质量属性源自并可以追溯到这一步展现的业务目标。因为这个原因,这些业务目标必须划分优先级。

3.软件架构方案演示

软件架构师介绍目前存在的系统架构计划。虽然在这个时候架构通常还没有被定义(特别是绿地系统),但是即使在这个早期阶段,软件架构师也往往会知道很多。例如,软件架构师可能已经知道被批准使用的技术,该系统必须与之交互的其他系统,必须遵循的标准,可以重复使用的子系统或组件等。

4.架构驱动程序的识别

主持人分享他们在步骤2和步骤3所得出的重要驱动因子的列表,并要求利益相关者澄清、添加、删除和修改。这里的想法是就提炼出来的软件架构驱动因子的清单达成一个共识。该清单涵盖了主要的功能需求、业务驱动因子、约束和质量属性。

5.场景头脑风暴

在这一步,每个利益相关者现在有机会提出一个场景来展示利益相关者对系统的需求和愿望。QAW主持人确保每个场景都有一个明确的刺激与反应。QAW主持人还要保证场景的可追溯性和完整性:对于步骤4中列出的每一个架构驱动因子都至少要有一个代表性的场景。步骤2中所有的业务目标都应该被覆盖。

6.场景整合

类似的场景被合理地整合起来。在步骤7中,利益相关者为他们最喜欢的场景投票。这种整合有助于防止选票被分散在几个基本上在表达相同关注点的方案上。

7.确定场景的优先级

排定优先级的方法是分配给每个利益相关者的票数为场景总数的30%。利益相关者可以将这些选票投给任何一个或多个场景。一旦所有的利益相关者投票完毕,结果就会被统计出来,场景按照人气排序。

8.场景细化

细化和详细描述最高优先级的场景。QAW主持人帮助利益相关者用六个场景的形式来表达:来源、刺激、工件、环境、响应及响应度量。

因此QAW的输出是排好了优先级的场景列表,与业务目标一致,其中优先级最高的场景已经被探索和细分出来。在一个简单的系统中或作为一个迭代部分进行时,QAW可以在短短的2~3小时内实行,当在一个以需求完整性为目标的复杂的系统中,时间可能长达2天。

效用树

即使没有利益相关者可以随时咨询,你仍然需要决定该做什么,以及如何对该系统所面临的许多挑战进行优先级排序。组织你的想法的一个方法是创建一个效用树,如下图所示,有助于详细说明你的质量属性目标,然后对它们进行优先级排序。

它的工作原理如下。先在一张纸上写上“效用”这个词。然后编写对你的系统构成效用的各种质量属性。例如,你可能知道,基于该系统的业务目标,系统最重要的品质是快速、安全、易于修改。然后,你会在“效用”下面写下这些单词。下一步,因为我们不知道这些术语实际意味着什么,所以我们描述我们最关心的质量属性。例如,虽然“性能”是模糊的,但“数据库事务的延迟”就不太模糊了。同样,“可修改性”是模糊的,“很方便地添加新的解码器”就不那么模糊了。

效用树的叶子节点代表场景,它提供了你刚才列举的质量属性的具体例子。例如,对于“数据库事务的延迟”,你可能会创建一个场景,如“1000用户在正常条件下同时更新自己的客户记录,平均延迟1秒”。对于“很方便地添加新的解码器”,你可以创建一个场景,类似“客户要求将一个新的自定义的解码器用2人·周的工作量添加到系统中并且没有副作用”。

最后,已创建的场景必须设定优先级。我们通过使用在两个维度上的排名技术,得到一个如下的优先级矩阵(其中单元格中的数字是从一组系统场景中得到的)来做出优先级的排序。

作为软件架构师,我们的工作是专注于本表的右下角(H, H):那些具有高商业重要性和高风险的场景。一旦我们令人满意地解决了这些场景,我们就可以开始解决(M, H)或(H, M)的,然后向上和向左,直到所有的系统场景都被解决(或者直到我们用完了时间或预算,通常是这种情况)。

应该指出的是,QAW和效用树是针对同一目标的两种不同的技术,找到最重要的质量属性需求并划分优先级,这些将成为最重要的架构驱动因子。但是没有任何理由在这些技术之间做出选择。两者都是有用的和有价值的,在我们的经验中,它们的优势互补:QAW往往更多地关注外部利益相关者的需求,而效用树则善于发掘内部有利于利益相关者的需求。让所有这些利益相关者满意和快乐对确保你的软件架构成功会大有帮助。

2.4.3 主要功能

功能是系统能够完成它所要做的工作的能力。相对于质量属性,系统结构的方式在正常情况下不会影响功能。你可以把一个给定系统的所有功能编码在一个单一的巨大的模块中,或者你可以把它灵活地分布在许多较小的、紧密结合的模块中。如果你只考虑功能,从外部看来系统的工作方式是相同的。然而,重要的是当你想改变这样的系统时会发生什么事。在前者的情况下,改变将是困难和昂贵的,在后者的情况下,它们应该更容易和用更低廉的成本执行。在软件架构设计方面,真正重要的问题是将功能分配给元素,而不是功能本身。在一个好的架构中最常见的变化是定位在一个单一的元素或几个元素上的,因此很容易实现。

在设计一个软件架构时,你至少需要考虑主要功能。主要功能通常被定义为实现业务目标,促进系统开发关键的功能。其他主要功能的标准也可以是意味着技术难度更高或者需要许多架构元素间的相互作用。有一个经验法则,你的用例或用户故事里大约10%可能是主要功能。

在设计一个软件架构时,你为什么需要考虑主要功能有两个重要的原因:

1.你需要思考如何将功能分配到元素(通常是模块)来推进可修改性或可重用性,并计划工作任务。

2.一些质量属性场景直接与系统的主要功能相连。例如,对于一个电影流媒体应用程序,主要的用例之一当然是看电影。这个用例可以与一个性能质量属性的场景关联,如“一旦用户按下播放键,电影应该在不超过5秒的时间内开始播放”。在这种情况下,质量属性场景直接与主要用例关联,所以做出支持这个场景的决定同时也需要决定如何支持其相关的功能。并不是所有的质量属性场景都像这样。例如,一个可用的场景可以涉及从系统故障中恢复,这种故障可能会发生在任何系统的用例正在执行时。

在软件架构设计中功能分配的决定为其余的功能要分配到模块中作为开发的进展建立了一个先例。这通常不是软件架构师的工作,这种活动通常是作为2.2.2节中描述的元素的交互设计过程的一部分。

最后,对功能分配的错误决定会导致技术债务的积累(当然,可能只有事后这些决定才会被发现是错误的)。这些问题可以通过使用重构解决,尽管这会影响项目进展的比例或速度(参看下面的引文“重构”)。

重构

如果你将一个软件架构(或其中的部分)重构,你所做的是保持相同的功能但改变一些你所关心的质量属性。软件架构师往往选择重构因为系统中的一部分很难理解、调试和维护。或者,他们可能会因为部分系统变得缓慢,或容易发生故障或不安全而重构。

在任何一种情况下的重构的目标都不是改变功能,而是改变质量属性响应(当然,增加功能有时也会跟重构混在一起,但这不是重构的核心目的)。很显然,如果我们能保持相同的功能,但改变架构以实现不同质量属性的响应,这些需求的类型相互之间是正交的,就是指它们都可以独立地变化。

2.4.4 架构关注点

软件架构的关注点包含额外的方面,这些方面需要被视为架构设计的一部分,但不会表达为传统的需求。有几种不同类型的关注点:

❑ 一般关注点。这些都是一个人在创建软件架构时处理的“宽泛”的问题,如建立一个整体的系统结构,将功能分配到模块,将模块分配到团队,代码库的组织、启动和关闭,并支持交付、部署和更新。

❑ 具体关注点。这些都是更细节的系统内部问题,如异常管理、依赖管理、配置、日志记录、身份验证、授权、缓存等。它们在多数应用程序中很常见。一些具体关注点会在参考架构中提到(参看2.5.1节),但其他的会是你的系统独有的。一些具体关注点也可能来自于之前的设计决策。例如,如果你以前决定使用一个用于Web应用程序开发的参考架构,那么你可能需要解决会话管理的问题。

❑ 内部需求。这些需求通常没有在传统的需求文档中明确指定,因为客户通常很少会提出它们。内部需求可能会涉及促进系统的开发、部署、运行或维护的几个方面。它们有时被称为“派生的要求”。

❑ 问题。这些是分析活动如设计审查(参看8.6节)的结果,所以它们可能不会在最初出现。例如,一个架构评估可能会发现一个风险,为了解决该风险需要在目前的设计中进行一些变化。

围绕架构关注点的一些决策可能是微不足道的或明显的。例如,你的部署结构可能是一个嵌入式系统的单处理器,或一个单应用程序的手机。你的参考架构可能会受到公司政策的限制。你的身份验证和授权策略可能会取决于你的企业架构,并在一个共享的框架中实现。然而,在其他情况下,为满足特定的关注点所需要做的决定可能就不太明显了,如在异常管理或输入验证或构建代码库时。

根据过去的经验,聪明的架构师通常会注意与特定类型的系统相关的关注点和做出设计决策来处理它们的需求。缺乏经验的架构师通常不太会意识到这样的问题,因为这些关注点往往是隐性的,而不是明确的。他们可能把这些关注点作为设计过程中的一部分来考虑,这往往会在后期导致问题。

架构关注点经常会导致引入新的质量属性场景。例如,“支持日志记录”这个关注点太模糊,需要作出更具体的描述。同客户提供的质量属性场景一样,这些场景也需要划分优先级。然而,对于这些场景,客户是这个组织中的开发团队、运营或其他成员。在设计过程中,软件架构师必须同时考虑由客户提供的质量属性场景和那些来自架构关注点的场景。

我们修订ADD方法的目标之一是提高将架构关注点作为一个软件架构审计过程的显式输入的重要性,如同我们将在第4~6章的例子和案例研究中强调的那样。

2.4.5 约束条件

你需要将开发中的约束作为架构设计过程的一部分来分类。这些限制条件可能表现为强制性技术,如你的系统必须要与之进行互操作或整合其他系统、必须遵守的法律和标准、你的开发人员的能力和可用性、最终期限是无法商榷的、与旧版本系统的向后兼容性等形式。技术约束的一个例子是开源技术的使用,而一个非技术性约束的例子是系统必须遵守《萨班斯-奥克斯利法案》或者系统必须在12月15日之前发布。

约束是指架构师很少或根本无法控制的决策。正如我们在第1章提到的,你的工作是为了满足:尽管你面临种种约束条件,尽你所能设计最好的系统。有时你可能可以通过争辩来释放一个约束,但在大多数情况下,你除了在约束条件下设计没有别的选择。