第2章 识别软件架构中的耦合

11月3日,星期三,13:00

Penultimate Electronics公司的首席架构师Logan在自助餐厅打断了一群分布式架构的架构师的讨论。“Austen,你又打石膏了吗?”

“不,只是个夹板,”Austen回答,“我在周末玩极限飞盘高尔夫时扭伤了手腕——差不多要好了。”

“那是?……无所谓了。我刚过来时,你们在讨论什么,讨论得那么激烈?”

“为什么有些人有时不用微服务里的Saga模式把事务串联起来?”Austen问道,“这样架构师就可以把服务拆分到他们想要的那么小。”

“但是你不是还需要在Saga模式中进行编排吗?”Addison问道,“那我们需要异步通信时该怎么办呢?而且,事务会变得多复杂?如果拆得稀碎,我们还能保证数据的准确性吗?”

“你知道,”Austen说,“如果使用企业服务总线(ESB),它可以帮我们管理大部分事情。”“我以为早就没人用ESB了——难道不应该使用Kafka来做这样的事情吗?”

“我以为早就没人用ESB了——难道不应该使用Kafka来做这样的事情吗?”

“它们甚至不是一回事!”Austen说。

Logan打断了愈演愈烈的对话:“它们本来就风马牛不相及,但哪个都不是银弹。像微服务这样的分布式架构是很复杂的,尤其是当架构师无法把所有互相作用的因素梳理清晰的时候。我们需要的是一种方法或框架来解决架构中的难题。”

“当然,”Addison说,“无论我们做什么,都必须尽可能解耦——我看到的所有东西都说架构师必须尽可能地拥抱解耦。”

“如果你那么做,”Logan说,“所有东西都彻底解耦,就没有东西可以和其他部分通信了——这样做软件可太难了!就像很多东西一样,耦合本质上不是坏的,架构师只需要知道如何恰当地应用它。”

在分布式架构中,架构师面对的最困难的任务之一就是厘清作用于其中的各方势力和权衡。发表意见的人们不断赞美“松散耦合”系统的好处,但是架构师怎么可能设计一个互不关联的系统?架构师可以设计粒度非常细的微服务来达到解耦,但是随之而来的编排、事务和异步就成了大问题。泛泛地建议“解耦”,但却没人指导我们如何在达成这个目标的同时构建能用的系统。

粒度和通信上的决策让架构师很为难,因为没有四海皆准的原则帮助他们下定决心——没有已知的最佳实践可以直接应用到现实世界里复杂的系统上。直到现在,在面对每个具体的情况时,架构师还是缺少合适的视角和术语认真地分析并做出最好的(至少不是最糟的)权衡。

为什么架构师在分布式架构中如此难以抉择呢?毕竟从上个世纪开始,我们就用很多相同的机制(例如消息队列、事件等)来开发分布式系统了。为什么到了微服务这里,复杂度增加了这么多?

这和微服务的基本理念息息相关,微服务是由限界上下文的灵感激发而来的。与设计分布式系统不同,开发以限界上下文为模型的服务需要一个微妙而又重要的更改——现在事务性成了架构考量中的头等大事。在微服务之前的很多分布式系统中,事件处理器通常连接同一个关系型数据库,使得它可以处理诸如完整性和事务这样的细节问题。将数据库移动到服务边界内部,使得数据问题变成了架构考量。

如前所述,“软件架构”是你在网上搜不到答案的东西。现代架构师必须掌握的一项技能就是做权衡分析。虽然有几个已经存在了数十年的框架[比如架构权衡分析方法(Architecture Trade-off Analysis Method,ATAM;https://oreil.ly/okbuO)],但是它们都没有关注架构师日常面对的现实问题。

本书重点关注架构师在面对特有的问题时,如何针对任意数量的情景做权衡分析。在架构中,纸上谈兵容易,难点在于细节,尤其是当复杂的部分交织在一起,让人更难去观察和理解其中的每一部分,和图2-1一样。

对于纠缠不清的问题,因为难以拆分各种关注点来独立解决,所以架构师很难进行权衡分析。因此,权衡分析的第一步是厘清问题的维度,分析哪些部分是互相耦合的,以及这些耦合对变化的影响。出于该目的,我们使用最简单的方式来定义耦合。

耦合

如果改变软件系统中的一部分可能带来另一部分的改变,则这两部分是耦合的。

图2-1:把头发编起来,让你分不清其中的某一股

通常,软件架构会产生多维度问题,多种势力以互相依存的方式相互作用。为了做权衡分析,架构师必须首先判断出需要权衡的因素有哪些。

因此,这里我们对软件架构中现代权衡分析的建议是:

1.找出彼此纠缠的部分;

2.分析它们的耦合方式;

3.通过其变化对于相互依存的系统的影响来评估利弊。

步骤看起来简单,难点潜伏在细节中。因此我们用一个分布式系统中最困难的(可能也是最通用的)问题之一作为例子,在实践中讲解这个框架,它和微服务相关。

架构师如何决定微服务的体量和通信方式?

给微服务选择合适的大小,看起来是个普遍问题——太小的服务造成事务性和编排问题,太大的服务会带来规模和分布式问题。

为此,本书接下来的部分在回答上述问题时,会一一解释如何从多方面去考量。我们提供新的术语来区分相似却不同的模式,用现实中的案例来演示如何应用这些模式。

然而,本书的总体目标是提供范例驱动的技巧,帮助你学习如何为领域里的特定问题做权衡分析。我们从定义架构量子以及两种耦合类型(静态和动态)开始。