软件架构:架构模式、特征及实践指南-读书笔记(1)

第二章 架构思维

2.1 架构与设计

为了使架构落地,必须打破阻碍在架构师和开发人员之间的所有障碍,从而使架构师和开发团队之间形成双向的强关联。如图2-3所示,架构师和开发人员必须在同一个虚拟团队中才能使架构落地。该模型不仅促进了架构师与开发人员之间频繁的双向沟通,而且还促使架构师为团队中的开发人员提供指导和培训。

2.2 技术广度

但是,随着开发人员过渡到架构师角色,知识的性质也会发生变化。架构师的价值很大一部分是广泛地理解技术,并且知道如何利用技术解决特定的问题。例如,作为一名架构师,知道一个问题的五种解比仅知道一种专业解更为有益。对于架构师来说,最重要的部分是金字塔的顶部和中间部分。如图2-6所示,中间部分与底部交汇处的长度代表了架构师的技术广度。
作为一名架构师,技术广度比技术深度更重要。因为架构师的职责就是根据功能做出与技术限制相匹配的决策,所以广泛了解各种解决方案是非常有价值的。因此,如图2-7所示,对于架构师而言,明智的做法是做到“博而不专”。如图所示,除了一些必要的专业知识,架构师要把时间在拓展行业内的不同知识上。

架构师应关注技术广度,以便在设计架构时有更丰富的工具箱。过渡到架构师角色的开发人员可能必须更改他们获取知识的方式。事实上,每个开发人员在职业生涯中都应该平衡知识的深度和广度。

2.3 分析权衡

程序员了解所有技术的优势是构师需要在其中找到平衡。

除了安全性问题外,图2-9中的“主题”方案仅支持同构契约。所有收到出价数据的服务必须接受相同的契约和同组出价数据。在图2-10中的队列方案中,每个使用者可以针对其所需的数据拥有自已的契约。例如,假设新的竞标历史服务需要在出价消息中加入要价信息,但是没有其他服务需要该信息。在这种情况下,需要修改契约,从而会影响使用该数据的所有其他服务。在队列模型中,由于队列是一个单独的通道,因此,单独的契约不会影响任何其他服务。
图2-9中所示的主题模型的另一个缺点是,它不支持监控主题中消息的数量以及弹性伸缩。但是,使用图2-10中的队列模型可以分别监控每个队列,并以编程方式将负载均衡应用于每个出价消费者,可以独立地进行伸缩。请注意,这种权衡是特定于某种技术的:高级消息队列协议(AMQP)可以支持程序化的负载均衡和监控,因为队列和交换(即生产者发送的内容和消费者收到的内容)是分开的。分析利弊后,现在选择哪个更好?答案仍然是看情况!表2-1总结了这些权衡。

第三章 模块化

3.2 度量模块化

模块化对架构师非常重要,研究人员创建了各种跨语言的度量标准来帮助架构师理解模块化。我们重点聚焦三个关键概念:内聚性、耦合和共生性。

3.2.1 内聚性

计算机科学家定义了一系列内聚性度量指标,将内聚性从最佳到最坏分为:

功能内聚性
        模块的每个部分都彼此相关,并且模块包含了功能所必需的所有内容。
依序内聚性/顺序内聚
        两个模块进行交互,其中一个模块的输出作为另一个模块的输入。
联系内聚性/信息内聚/通信内聚
        两个模块构成了一条通信链,其每个模块都基于信息进行操作或有助于某些输出。例如,添加一条记录到数据库和基于该信息生成电子邮件。
程序内聚性
        两个模块必须以特定顺序执行代码。
时间内聚性
        模块基于时序依赖。例如,许多系统有一些看起来不相关的东西,必须在系统启动时对其进行初始化。这些不同的任务具有时间内聚性。
逻辑内聚性
        模块内的数据在逻辑上相似,但在功能上无关。例如,存在一个模块,该模块可以从文本、序列化的对象或流中转换信息。这些操作是相似的,但功能却大不相同。每个Java项目几乎都会出现Stringutils的包(一组在String上操作但在其他情况下都不相关的静态方法)是这类内聚性的常见示例。
偶然内聚性
        除了位于相同的源文件中,模块中的元素互不相关。这是内聚性的最负面形式。

CustomerMaintenance(客户维护模块)

  • 添加客户
  • 更新客户
  • 获得客户
  • 通知客户
  • 获得客户订单
  • 取消客户订单

也可以分成两个单独的模块,比如这样:

客户维护模块

  • 添加客户
  • 更新客户
  • 获得客户
  • 通知客户

订单维护模块

  • 获得客户订单
  • 取消客户订单

哪个是正确的结构?答案仍然是:看情况:

  • 订单维护模块只有这两项操作吗?如果是这样,将这些操作归还给客户维护模块可能挺讲得通的。
  • 是否期望客户维护模块包含更多的功能,从而鼓励开发人员寻找机会提炼行为?
  • 订单维护模块是否需要掌握大量的客户信息,以至于将两个模块分开需要高度的耦合才能正常运行?这与上文LarryConstantine的引用契合。

这些问题代表了软件架构师工作的核心,是一种权衡分析。

3.2.2 耦合

幸运的是,我们有更好的工具来分析代码库中的耦合,其原理一部分基于图论:由于方法的调用和返回可形成调用图,因此使基于数学的分析成为可能。

第四章 现有的架构特征

明确非领域设计的某个注意事项
        在设计应用时,需求指定了应用应该做什么,架构特征指定了成功的操作和设计标准,涉及如何实现需求以及为何做出某些决策。例如,一个常见的重要架构特征为应用指定了一定的性能水平,而这通常不会出现在需求文档中。更相关的是,没有需求文件会指出“防止技术债务”,但这是架构师和开发人员的常见设计注意事项。我们在5.1节中深入讨论了显式特征和隐式特征之间的区别。


影响设计的某些结构项
        架构师试图在项目上描述架构特征的主要原因与设计注意事项有关:此架构特征是否需要特殊的结构才能成功?例如,安全实际上是每个项目的顾虑,所有系统都必须在设计和编码过程中符合预防措施的标准。但是,当架构师需要设计一些特殊的功能时,它会上升到架构特征的层级。在示例系统中,考虑两种围绕付款的情况:

        第三方付款处理器
        如果集成点处理付款明细,则该架构不需要特殊的结构注意事项。设计应包含标准的安全规范,例如加密和哈希,但不需要特殊的结构。

        应用内付款处理
        如果正在设计的应用必须处理付款,则架构师可以为此目的设计特定的模块组件或服务,从结构上隔离关键的安全问题。如此一来,架构特征对架构和设计就都有影响。

4.1 部分已罗列出来的架构特征

4.1.1 运营性架构特征

运营性架构特征涵盖了性能、可伸缩性、弹性、可用性和可靠性等功能。表4-1列出了一些运营性架构特征。

4.1.2 结构性架构特征

架构师还必须关注代码结构。在许多情况下,架构师应对代码质量问题(例如良好的模块化、组件之间的受控耦合、代码可读性以及许多其他内部质量评估)负全责。

4.1.3 跨领域架构特征

尽管许多架构特征属于易于识别的类别,但也有许多特征属于不合理的分类,但却构成了重要的设计约束和考虑因素。详见表4-3。

第五章 识别架构特征

5.2 从需求中提取架构特征

一些架构特征来自需求文档中的显式声明。例如,显式的预期用户数量和规模通常出现在领域问题中。其他方面则来自架构师固有的领域知识,这是领域知识始终对架构师有益的众多原因之一。例如,假设一名架构师设计了一个处理大学生班级注册的应用。为了简化计算,假设学校有1000名学生和10个小时的注册时间。架构师是否应该设计一个假设规模一致的系统,并隐式地假设学生在注册过程中会随着时间的推移平均分配自已的时间?或者,基于对大学生习惯和倾向的了解,架构师是否应该设计一个系统在最后10分钟内处理所有1000名学生的注册请求?任何了解学生是有多拖延的人都知道这个同题的答案!这样的细节很少会出现在需求文档中,但是它们确实可以为设计决策提供依据。

5.3 案例研究:硅三明治

为了说明几个概念,我们使用架构kata。为了展示架构师如何从需求中获取架构特征我们将举例介绍硅三明治卡塔(Silicon Sandwiches kata)。
描述
        一家全国连锁的三明治店希望开启在线订购(除了当前的呼叫服务之外)。
用户
        目前数千,也许一天可达到百万级别。
需求

  •         用户将会下订单,然后给定一个取三明治的时间,以及展示一个去商店的路线(商店必须集成多个包含交通信息的外部地图服务)。
  •         如果商店提供送货服务,请向用户发送配送司机和三明治的信息。
  •         移动设备可访问性。
  •         提供全国每日促销/特价。
  •         提供当地每日促销/特价。
  •         接受在线付款、当面付款以及交货时付款。

其他内容

  •         三明治店专营权,每个店主拥有不同的所有者。
  •         母公司近期计划向海外扩张。
  •         公司目标是雇用廉价劳动力以最大限度地提高利润。

在这种场景下,架构师将如何得出架构特征?需求的每个部分都可能对架构的一个或多个方面有影响。架构师不会在这里设计整个系统(解决领域问题仍需要花费大量精力来编写代码)。相反,架构师需要寻找影响设计的架构特征,尤其是结构上的影响。首先,将候选架构特征分为显式特征和隐式特征。

5.3.1 显式特征

显式的架构特征出现在需求规范中,作为设计的必要部分。例如,购物网站可能希望支持特定数量的并发用户,这是领域分析师在需求中指定的。架构师应考虑需求的每个部分,以了解其是否有助于架构特征。但是首先,架构师应该考虑关于预期指标的领域级别的预测,如卡塔的“用户”部分所述用户数量应该引起架构师的注意,其中最重要的细节之一是:目前有数于名用户,也许有一天会达到百万人(这是一家雄心勃勃的三明治店!)。因此,可伸缩性(在不严重降低性能的情况下处理大量并发用户的能力)是最重要的架构特征之一。请注意,问题说明并未显式要求可伸缩性,而是将该要求表示为预期的用户数量。架构师必须经常将领域语言解码为等效的工程技术。
但是,我们可能还需要弹性(处理突发请求的能力)。这两个特征通常看起来是混在一起的,但是它们有不同的约束。可伸缩性看起来如图5-1所示。

另一方面,弹性可衡量流量的突发,如图5-2所示。
一些系统是可伸缩的,但不是弹性的。以一个旅馆预订系统为例。如果没有特别的销售或活动,用户数量可能会保持一致。相反,考虑音乐会门票预订系统。随着新门票的发售,狂热的粉丝将涌入该场地,这需要高度的弹性。弹性系统通常还需要可伸缩性:处理突发事件和大量并发用户的能力。

弹性要求未出现在“硅三明治”要求中,但架构师应将其识别为重要的考虑因素。需求有时会直接说明架构特征,但有些潜伏在向题域内。例如,一个三明治店整天的流量是否一致?还是进餐时间会十分拥挤?儿乎可以肯定是后者。因此,好的架构师应该能够识别这种潜在的架构特征。

架构师应依次考虑所有这些业务需求,以查看架构特征是否存在:

1.用户将会下订单,然后给定一个取三明治的时间,以及展示一个去商店的路线(商店必须集成多个包含交通信息的外部地图服务的选项:
外部地图服务暗示集成点,这可能会影响可靠性等方面。例如,如果开发人员构建的系统依赖于第三方系统,但是调用失败,则会影响调用系统的可靠性。但是,架构师还必须警惕过度指定的架构特征。如果外部交通服务中断了怎么办?硅三明治网站是否应该失败,还是提供一个没有交通信息的低效率产品?架构师应始终防止在设计中带入不必要的脆弱性。


2.如果商店提供送货服务,请向用户发送配送员和三明治的信息。
似乎不需要特殊的架构特征来支持此要求。


3.移动设备的可访问性。
此要求将主要影响应用的设计,需要构建一个可移植的Web应用或几个原生Web应用。考虑到预算的限制和应用的简单性,架构师可能会认为构建多个应用过于刻薄,因此设计改为了对移动设备进行优化的Web应用。因此,架构师可能希望为页面加载时间和其他移动敏感特性定义一些特定的性能架构特征。请注意,架构师不应在这种情况下独自行动,而应与用户体验设计师、领域利益相关者以及其他有关部门合作,共同审核此类决策

4.提供全国性的每日促销/特价。


5.提供当地每日促销/特价。
这两个要求都指定了促销和特价中的可定制性。请注意,促销要求还隐含了基于地址的自定义交通信息。基于所有这三个要求,架构师可以将可定制性视为架构特征。例如,微内核架构等架构风格通过定义可插件架构,可以很好地支持自定义行为。在这种情况下,默认行为会出现在核心中,并且开发人员将根据位置通过插件编写可选的自定义部件。但是,传统设计也可以通过设计模式(例如模板方法[Template Method))来满足此要求。这种难题在架构中很常见,要求架构师在各种竞争方案之间不断取舍。关于特定权衡的讨论详见5.3.2节。


6.在线、当面或在收货时付款。
在线支付暗含安全性,但此要求中没有任何一项表明需要特别高级别的安全性。

7.三明治店是专营店,每个店都有不同的所有者。
此需求可能会对架构增加成本限制一一架构师应检查可行性(诸如成本、时间和员工技能等约束)以查看是否需要简单架构或牺牲某些特质的架构。

8.母公司有近期向海外扩张的计划。
此需求意味着国际化。有许多设计技术可以满足这一要求,这些技术不需要特殊的结构即可适应。但是,这肯定会推动设计决策。

9.公司的目标是雇用廉价的劳动力以使利润最大化。
该需求表明易用性将很重要,但同样,它更关注设计而不是架构特征。

我们从上述需求得出的第三个架构特征是性能:没有人愿意从性能不佳的三明治店购买,尤其是在高峰时段。但是,性能是一个微妙的概念一一架构师应设计什么级别的性能?我们在第6章中介绍了性能的各种细微差别。

我们还希望结合可伸缩性数字定义性能数字。换句话说,我们必须建立一个没有伸缩的性能基准,并确定给定数量的用户可接受的性能水平。通常,架构特性会相互影响,从而迫使架构师在架构特质之间进行来回定义。

5.3.2 隐式特征

许多架构特征并不会在需求文档中指定,但它们对设计有重要的影响。该系统可能要支持的隐式架构特征是可用性:确保用户可以访问三明治站点。可靠性与可用性密切相关:可靠性确保网站在交互过程中保持正常运行一没有人愿意从总是断开连接的站点购买产品。

安全性是每个系统中的隐式特征:没有人愿意创建不安全的软件。但是,可以根据重要程度对其进行优先级排序,这说明了我们定义的内在联系。如果安全性影响了设计的某些结构并且对应用至关重要,则架构师会将安全性视为架构特征。

对于“硅三明治”,架构师可能会假设付款应由第三方处理。因此,只要开发人员遵循一般的安全习惯(不将信用卡号作为纯文本传递,不存储太多信息,等等),架构师就不需要任何特殊的结构设计来确保安全性,在应用中进行良好的设计就足够了。每个架构特征相互影响,其常见的陷阱是过度指定架构特征,这与未充分说明架构特征一样有害,会使系统设计变得过于复杂。

“硅三明治”需要支持的最后一个主要架构特征是可定制性,它包括需求中的儿个细节,请注意,向题域中提供了自定义行为:配方、本地销售和可能在本地被覆盖的指示。因此,架构应支持自定义行为的能力。通常,这属于应用设计的范围。但是,正如我们的定义所指出的那样,部分依赖自定义结构的问题域可以将其移入架构特征领域。但是,此设计元素对于应用的成功并非至关重要。重点是,选择架构特征时没有正确的答案只有错误的答案(正如Mark在他的著名语录之一中指出的那样):


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

架构师可以设计一种无法在结构上适应可定制性的架构,需要应用本身的设计才能支持该行为。架构师不应该过分强调发现一组完全正确的架构特征一一开发人员可以通过多种方式实现功能。但是,正确识别重要的结构元素可能有助于产生简化或更优雅的设计。架构师必须记住:没有最佳的架构设计,只有最差可用的权衡取舍。

架构师还必须考虑这些架构特征的优先级,以尝试找到最简单的必需集合。一旦团队在确定架构特征上走出了第一步,一个有用的练习就是尝试确定最不重要的一个一一向一下自已,如果必须消除一个,那会是什么?通常,由于许多隐式架构影响应用的成功,因此架构师更倾向于剔除显式架构特征。我们通过定义什么对应用的成功至关重要,可以帮助架构师确定应用是否真正需要某种架构特征。通过尝试确定最小应用,架构师可以帮助确定其关键需求。对于“硅三明治”,我们确定的哪个架构特征最不重要?同样,不存在绝对正确的容案。然而,在这种情况下,自前解决方案可能会放弃可定制性或性能。我们可以消除可定制性,并将这种行为实现为应用设计的一部分。在运营性架构特征中,性能对于成功的影响最小。当然,开发人员并不是要构建具有糟糕性能的应用,而是要建立一个不将性能置于其他特征的优先级(例如可伸缩性或可用性)之上的应用。

  • 20
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值