当评估利弊时,架构师必须保证联系上下文来做决策。不然,外部因素会不合时宜地影响到分析。经常,一个解决方案有很多有利的地方,但缺乏致胜的能力,以致无法成功。架构师必须确保他们权衡的是一组正确的利弊,而不是所有可能的选项。
在一个分布式架构中,架构师可能正在尝试决定,对于一个公用功能到底是该用共享服务还是共享库
看起来选择共享库的方式的好处是最多的,总的来说这个矩阵的结果是倾向于此的。然而,这个决策恰好凸显了“跳出上下文”的问题。当有额外的上下文信息补充进来时,问题就变得清晰了,决策条件改变了:
“整个应用应用系统,为了利用不同语言的变成优势,由3种不同的编程语言写成的服务,性能和容错性不是问题,我们只关心公共功能的变更管理能力”
继续深入研究,不仅仅是服务和库之间的通用问题,而是针对这个场景的真实上下文。记住,通用方案在真实世界的架构里几乎都是没用的,必须附上特定场景的上下文才行。
这个过程强调了两个重要的观察。首先,寻求对于决策的最佳上下文,帮助架构师过滤多余的选项,可以极大地简化决策过程。软件宗师的共同建议就是“拥抱简单的设计”,但从来没解释过如何做到这一点。为决策寻求正确的最小上下文,帮助架构师减轻思考负担,很多时候就可以达到简化设计的目的了。
理解架构的迭代式设计的重要性是非常关键的。画一些架构方案的简单图示,在上面做一些“如果这样会如何”的定量分析,来观察不同架构维度之间如何互相影响。使用迭代式设计,架构师可以研究各种可能的方案,发掘引导决策的上下文。
支付的例子
架构师不应该不考虑为特定方案增加价值的相关驱动,而一味地做出决策因素。在决策过程中加入那些领域驱动因素,可以帮助架构师筛选方案,关注真正重要的利弊分析。举例来说,考虑架构师的这个决策是应该为不同的支付方式创建单独的服务,还是一个支付服务
架构师可以选择一系列集成因素和分解因素来辅助决策。然而,这些因素都是通用的——架构师需要通过对场景建模来在决策中加入更多的细节。举例来说,更新信用卡处理服务。在这个场景中,独立的服务提供了更好的可维护性、可测试性和可部署性,都是基于这些服务在量子级别的隔离。然而,独立服务的劣势也在于常常需要在服务间复制代码来阻止静态量子耦合,这会抵消其带来的好处。
在第二个场景中,当系统中增加一种支付方式时,架构师该如何为其建模
架构师增加了一个奖励积分支付类型,以观察它对相关的架构特征有何影响,突出独立服务带来的可扩展性的好处。到此为止,独立服务看起来还是很可行的。
场景三,多种支付方式合用
在这个场景中,架构师开始深入了解这一决策中涉及的真正的利弊分析。利用独立服务需要工作流的协调,最好通过编排器来处理。然而,正如我们在第11章讨论过的,使用编排器很可能会对性能有负面影响,并且加大了保持数据一致性的难度。架构师可以避免编排器,但是工作流涉及的逻辑必须放在什么地方——记住,语义耦合只会随着实现而增加,并不会减少。
为这三种场景建模后,架构师发现真实世界的利弊分析最终归结到什么对你更重要:性能和数据一致性(统一支付服务),还是可扩展性和敏捷性(独立服务)。用通用和抽象的方式思考架构问题,无法让架构师走得更远。就如同架构问题往往能完美避开通用解决方案,对于架构师来说,重要的是自己掌握建模相关的领域能力,以便做出更好的利弊分析和决策。
同步通信还是异步通信
同步的方案,编排器会使用同步REST调用来与工作流协调者通信;而异步方案使用消息队列来实现异步通信。
信用卡流程中同步通信和异步通信的优缺点分析
为这些场景建模之后,架构师可以为干系人做出一个底线决策:什么是更重要的,保证信用审批流程立即开始,还是响应性和容错性?跳过难以理解的技术细节,帮助非技术领域的干系人关注结果,而不是设计决策,也避免他们陷入无边的细节中。
最后,架构师应该去询问利益相关方(运维、企业架构师、业务分析师等)来决定这些利弊中的哪些方面更重要。有时架构师会选择不去传播一些东西,而是被迫扮演一个说反对意见的人,尤其是一些没有明显优势的东西。但技术也有自己的粉丝,甚至是最狂热的粉丝。
一旦我们都学会如何分离维度、做利弊分析,就学会了如何具象我们的架构了。谁关心那些通用的东西呢?如果我们可以把利弊分析所需的维度减少到能够建模的数量,就为我们的生态系统获得了无价的知识。你知道的,结构性工程师有无数数学和其他预测性工具,但是打造这些又难又费钱。我总是说,测试是软件开发工程严谨性的保障。尽管我们没有其他工程师所拥有的数学知识,但我们可以逐步打造和测试我们的方案,实现更多的灵活性,并利用更灵活的媒介的优势。用客观结果进行测试可以使我们的利弊分析从定性转为定量——从探索到工程化。关于我们独特的生态系统,能够了解到的具体事实越多,分析就越精确。