注重实效的架构师——大胆行前人未行之路
原文链接:The Pragmatic Architect - To Boldly Go Where No One Has Gone Before
本文首次发表在 IEEE Software ,并由 InfoQ 和 IEEE 计算机协会为您引进。
是什么让架构师们精通自己的技艺?熟练的架构师是如何进行设计的?一次次,有人问起我这些问题,而我也不止一遍的问我自己。很明显,这并不只是软件工程过程、设计方法、技术或是编程的专业程度所决定的。很多架构师具备令人钦佩且完备的技术知识,这确实是使设计成功的必要条件。但是,还是有很多的软件项目失败了,或是在项目的架构中遭受到了严峻的挑战。掌握此道的关键在于架构师是以什么方式实现设计,他们重视什么,他们关注哪些方面以及在这些方面努力着。
(缺失的)线条能告诉我们什么
图1展示了摘自以前项目的一张高层次图表,那个项目的架构师创建了该图以阐述系统的基本设计。
该图大致描述了系统的关键组件、各组件的职责以及它们核心的模块化原理。架构师使用这些概要图来交流设计,以管理开发的过程,并以此讨论它们在业务方面产生的影响,例如:成本、时间和精力等。然而,一段时间后,管理层会想要知道项目为什么会有重大延期以及预算为什么会超支,他们无法从架构师的报告中获得任何提示或指标。
问题来自于该图上的点状黑线以及那些“尚未显示的线条”。这些点状黑线表示了该项目开发的一个专有的消息中间件。在图上无法找到所罗列的这些组件之间的通信关系,特别要强调的是,这些关系是通过这个中间件运作的。该项目的架构师并不认为值得对这些方面值建模或报告,因为从他们的视角来看,它们代表的是基本技术的基础设施,并不会有助于系统的领域和业务用例。然而,中间件的开发消耗了大量预算,因为这个过程中涉及到很多健壮性和性能的问题,我们不得不从系统的其他部分中抽调最好的开发人员来解决这些缺陷。另外,支撑这些中间件问题也需要在领域组件中付出相当多的努力。
图1.一幅用于和业务利益相关者交流关键设计的高层次架构概要图
事物间的设计
过往的经验显示,系统的架构师们常常将注意力过多地集中于一些“明显”的事物上:用户接口、领域特定组件、数据管理以及持久化等。然而架构的问题并不在组件之内,而是在组件之间:与其他系统之间的接口,交互以及集成——包括底层的技术基础设施。但是架构规范里几乎不涉及这些方面,所以在这些方面发生问题之前,架构师和开发人员都不会给予关注。
与此相反,注重实效的架构师们更关注事物之间的事物——就是说,组件之间的事物以及代码行(LOC)之间的事物,例如标准数据类型背后的领域思想。当然,他们也会指导系统实际组件的设计,但是当指导通常足够充分以支持更进一步拆分并且可由开发团队实现时,这些事物之间的事物实际上需要架构师们亲手处理。而且必须是他们,这是他们的领域——特别是在我们还不确定如何实际地设计这些“事物”时。下面让我们去探究一下注重实效的架构师们的秘密。
发现隐藏的领域概念
最近我在我们的几个系统的代码上运行tag-cloud生成器。我想通过这种方式对这些系统中重要的领域概念有个大致的印象。出人意料的是,在这些系统中最顶端的数据类型是 string 和 int。然而我怀疑是不是真的如此,因为在针对工业工程或能源管理的系统中,其他概念会更加重要:设备、电力线、传感器、制动器、标签、警报等等。当我更深入地查看代码时,就发现了这些领域概念——但是它们分散在表面上几乎不怎么相关的诸如 string 和 int 这样基本类型的配置中。
如此隐藏的领域概念可能需要开发者们花费相当多的努力来理解和实现系统,以保证产品的质量。我如何才能知道一个 int 实际表示的是某个特定的领域概念?我如何保证在某个特定的计算上下文中使用 int 时会执行特定领域概念的契约?我不能,除非通过注释和约定,其他的都对实践没有实质帮助。图2展示了摘自导致 Ariane 5 在其 1996 年首飞时坠毁的(标有注释的)代码片段,其根本原因是源于对整型数溢出的保护不足。
图2. 摘自 Ariane 5 的代码片段。因为对整型数溢出的保护不足导致了 Ariane 5 在其 1996 年首飞时坠毁。
我们有足够的勇气说,如果开发者以定义好的方式使用契约,将速度建模成一种合适的类型,那么 Ariane 5 的软件错误原本是可以避免的。然而这个例子也很好的把握了显式建模的重要性。
即使领域概念很明显、很清楚,但也可能埋藏在无数的细节之下。举个例子,我曾经研究过一个系统的复杂性。该软件包含了一个命名服务,该服务拥有一个包括 300 多种方法的接口。该服务的实际契约已经几乎看不清楚,而开发者们需要非常努力才能正确和有效地使用它。有分析表明,不超过 20 个方法就完全可以说明一个服务的必要契约。
注重实效的架构师因此会非常重视并做好相关的工作,在他们的架构中对所有领域概念进行明确的描述,例如那些常被粗粒度描述的组件,细微的特定领域的数据类型,有意义的接口,等等。在对概念的建模中,注重实效的架构师总是专注于精简而避免混乱或复杂,并着重强调概念的本质。这样一来,系统中那些隐藏的概念或是相关的性能都会立即明朗起来,这将会帮助开发人员更好的识别它们,并把它们看作独特的、明确的、有意义的类型(types)。用途(Usage)转变成类型——对于创建有表现力的软件设计和健壮的实现来说是一种重要的实践。
在事物衔接之处
什么是架构?Eoin Woods 对于该问题思考了相当长一段时候后找到了一个答案。“那些很多被架构师每天在使用着的思想:框架,代理,层次,接口,消息通知,连接器…这都是与间隙(gaps)相关的![...] 架构是一种用于连接软件设计师们一起工作的粘合剂,共同创造一个弹性的、灵活的、可扩展的以及最终可用的系统。”
在这个结论中包含着很多道理。我确定所有人都知道关于组件接口不支持工作流,而我们系统不得不支持的事。在很多项目中,集成——无论是系统集成还是“仅仅”是系统构成组件的集成——是成本最高的地方。而间隙(gaps)是指事物间衔接处的空间,组件交互的地方——没有一方会对其负责,除了注重实效的架构师!所以设计简洁及有意义的组件接口来支持组件间工作流的实现确实是不平凡的任务。在组件间定义符合用户在系统上执行任务那样的交互是一件困难的事。甚至更难的是将一个系统与其他系统集成,使其在不丧失任一相关系统独立质量的情况下支持跨系统的工作流。你可以快速浏览一下支持该结论主题方面的模式著作。
“空隙(void)”事实上需要架构师更多的关注和亲身实践。然而重点并非事物之间的适配——接口,交互,组件,系统——这些肯定是要连接的;重要的是随着对工作流的支持之后这些事物之间的协调性。目标是最精益(leanest)的适配,并且最深刻(impressive)的映射!这就是架构产生的地方;此处的决策将对系统的生命周期成本产生非常大的影响。本段开篇提到的战争故事也就恰好定格在这里:事物在哪里衔接。
我们可以对独立系统间那些“在中间(in-between)”的所有类型的工件的设计思想进行扩展——举个例子,驻留于不同计算节点上,在不同的进程中或是在不同的线程中各个部分。我曾看到过一些失败的项目,之所以失败是因为在他们的处理实体(processing entities)之间粘合的时候采用了一种幼稚的方式。其实在跨越计算机、进程和线程的分布式实体之间建立工作交互比较简单。这就是对设计的掌控,无论如何,创建(分布式)进程之间的交互可以最小化网络交互和对线程间同步的需求。
以不确定作为驱动
我们都会抱怨那些模糊的系统需求。然而纵使业务的利益相关者们进行了最仔细的需求捕获工作,这都无法完全解决所有的歧义和不确定性:这就是事实。同样,软件工程师可能需要针对特定的需求费心选择各种可选解决方案,并在讨论该采用哪种方案上花费大量时间。但是架构师则必须要面对来自设计、开发以及交付系统带来挑战,并且在系统发布和后续的整个生命周期中都能保证满足相关的业务需求。系统开发的时间越长,系统的生命周期越长,不确定性也就越大。
在这种形势下,架构师们会选择一种最典型的规避方式,那就是泛型——它可以最大程度地带来灵活性。架构通过弹性机制来应对过载,这在理论上支持所有可想象的系统配置,但是并不能满足任何有意义配置的具体(非功能性)需求。
注重实效的架构师能察觉到危险地带并以不确定性为驱动来做好决策。首先,他们承认需要在各种可选项中做好选择,并针对他们设计中的可变方面做出工作,以此来限制变化或选择造成的影响。他们会深入探索系统的使用场景,以此来澄清需求中的不确定性或对一种特殊设计选项的选择,从而实现出场景的原型或可运行骨架系统(walking skeleton)的一个部分。并且,他们非常欢迎来自于原型和可运行骨架系统的循环反馈,以驱动或调整他们的决策。架构师们重复地在事物之间(本例中是指在有歧义和不确定的需求或可选设计选项之间)进行着设计。
本文的标题是“大胆行前人未行之路”,架构设计恰好就需要如此。来到事物之间:在代码行上或其间,发现隐藏的领域概念;在你的系统和其他系统的组件之间,可以引导你设计好接口和工作流;在不确定性及可选项之间,驱动你的决策。架构师的工作就是去尽早地发现这些“在中间的”事物,使它们更加明确,从中做出决策。以上这些加上扎实的有关架构方法和技术的专业知识,以及谨慎的实践,就是对架构的精通之道:在软件系统的痛点上进行深思熟虑并最终决定它的成败。
参考文献
- J-J. Levy, "Un Petit Bogue, Un Grand Boum!" (in French), 2010.
- J-L. Lions et al., "Ariane 501 Inquiry Board Report," 1996.
- E. Evans, Domain Driven Design, Addison- Wesley, 2004.
- F. Buschmann and K. Henney, "Five Considerations for Software Architecture, Part 1, IEEE Software, vol. 27, no. 3, 2010, pp. 63-65.
- E. Woods, "Architecting in the Gaps," 2011.
- F. Buschmann, "Unusable Software Is Useless, Part 1," IEEE Software, vol. 28, no. 1, 2011, pp. 92-94.
- F. Buschmann, K. Henney, and D.C. Schmidt, Pattern-Oriented Software Architecture-A Pattern Language for Distributed Computing, John Wiley and Sons, 2007.
- G. Hohpe and B. Woolf, Enterprise Integration Patterns-Designing, Building, and Deploying Messaging Solutions, Addison-Wesley, 2003.
- F. Buschmann, "Learning from Failure, Part 2: Featuritis, Performitis, and Other Diseases," IEEE Software, vol. 27, no. 1, 2010, pp. 10-11.
- K. Henney, "Use Uncertainty as a Driver," 97 Things Every Software Architect Should Know, R. Monson-Haefel, ed., O’Reilly, 2009, pp. 321-361.