软件架构的一致性

在Brooks的最新力作《设计原本(The Design of Design)》一书中,提及“一致性”对软件的重要性。他认为:“一致性应该是所有质量原则的根基。好的架构应该是直接的,人们掌握了部分系统后就可以推测出其他部分”。这种所谓“窥一斑而知全豹”的要求,实则是要求保持风格的一致性。

  风格的一致性

  许多软件公司都会根据自身情况并结合业界规范制定符合本公司情况的编码规范。例如对命名、格式等诸多风格的要求。这种编码风格的一致性是相对容易实现的。Brooks提到的一致性,更多地是体现架构风格的一致性。那么,什么是架构风格(Architecture Style)?在微软撰写的Application Architecture Guide 2.0一书中,将架构风格定义为一组原则的集合,是为系统家族提供抽象框架的粗粒度模式。架构风格可以改善设计,促进重用,为常见的问题提供可靠的解决方案。而Mary Shaw对架构风格的定义则显得更加晦涩一些:“架构风格根据结构组织的模式定义系统种类。更具体地说,架构风格定义组件和连接器类型的词汇及它们如何进行组合的一组约束。”显然,Mary Shaw认为架构风格定义了架构组成元素的结构以及它们必须遵循的一些原则和约束。

  架构风格可能是对一些架构模式的运用,然而在运用模式之前,首先需要分辨系统的类别。例如系统如果是业务复杂的企业应用,那么就领域层而言,就应该选择领域模型(Domain Model),通过有效地利用对象、组件和服务保证系统的可重用性与可扩展性。而一旦选择了领域模型,那么在通常情况下,就不应该在同一个系统的其他模块中采用事务脚本(Transaction Script)模式。否则,我们就违背了风格的一致性。

  我们常常会将选定的架构风格作为整个系统架构的核心框架。了解了架构风格,就可以有助于了解整个软件系统。例如,对于数据分析器系统而言,它的核心逻辑是输入数据流、输出数据流与分析算法之间的协作。由于分析算法是对数据流的一种筛选和过滤,我们选定的架构风格是采用管道-过滤器(Pipes and Filters)模式。那么,在了解数据分析器的架构时,只要掌握了管道-过滤器模式的核心思想,就能够快速把握系统架构的核心。这有利于系统知识的延续,并保证整个系统的一致性。

  当然,架构风格的选择会因为关注点的不同,产生不同的解决方案。例如,从部署的角度来看,我们可能需要选择分布式部署的物理架构;而对于同一个系统而言,由于需要考虑消息的传递,我们又可能选择消息总线的方式。这与风格的一致性并不矛盾。我们只需保证在同一个关注面上,保持一致的风格即可。

  解决方案的一致性

  风格的一致性属于软件架构的层面,与之相似的是保证解决方案的一致性。在整个系统架构中,解决方案必须是一致的,否则就可能导致混乱的架构与代码。那么,怎样才能保证解决方案的一致性呢?首要条件是在进行系统架构之前,我们必须根据系统与团队的情况,制定被团队成员广泛接受的架构原则。例如,我们可以为系统制订分层指导原则。对于领域层,我们遵循DDD的要求,为领域模型确定实体、值对象、聚合根、服务、工厂与资源库之间的明确划分,并明确地指出它们应该具备的特征。我们要求应用服务层不应包含任何业务逻辑,只负责UI与领域层之间的消息传递,并可调用基础设施中公共模块的数据验证、缓存和安全等功能。我们要求应用服务层不应保留业务对象的状态,仅仅负责协调应用的活动,并要求所有公开在外的应用服务均定义为接口。

  在制订了这样的架构原则后,我们就可以统一系统的解决方案。例如遵循之前的分层指导原则,我们可以获得如下的针对领域层的统一解决方案:

  如果我们没有保证解决方案的一致性,并为此制订统一的架构原则,就会使得团队人员根据自己的意愿来随意选择解决方案。即使某个开发人员选择的解决方案或许是最优的,但由于系统存在多种不同的解决方案,就可能使得整个系统陷入混乱之中。例如,在我曾经看到过的一个系统中,就存在不一致的数据库访问解决方案。在同一个系统中,模块A使用了Spring的JDBC模板访问数据库,而模块B则直接使用了JDBC。还是在这个系统,模块C通过使用JDK的map来缓存频繁读取的值,而在模块D中却又使用了开源的EhCache作为缓存。

  为了保证解决方案的一致性,除了需要事先制订统一的架构原则之外,前面提到的风格一致性也能够给予统一的指导和约束。事实上,我们可以将解决方案的一致性理解为对风格一致性的一种实现。除此之外,团队成员之间的协作与沟通,以及必要的架构评审与代码走查,都能够在一定程度上避免解决方案的不一致。

  形式的一致性

  保持系统架构在形式上的一致性,常常是架构师的有意为之,其目的是希望保持架构的简单性。最能体现形式一致性的一个原则是“惯例优于配置”。这里所谓的“惯例”,可以理解为框架对实现的一些约束。我们可以根据事先制订的默认规则,通过反射技术完成对象的创建,对象的协作,甚至是应用程序的组装。“惯例优于配置”的关键,就在于它规定了形式上的一致性。以Ruby on Rails为例,通过事先确立Model、View和Controller的目录结构与命名规范,强迫开发人员对系统进行MVC的划分,并严格遵守框架制订的规范。在程序运行时,Rails会将分离的部分组装在一起。组装的过程默认按照命名约定与惯例进行,在一般情况下就不需要任何外部的元数据配置信息。下图是Rails的MVC架构: 

  当客户端向服务端发出请求后,Dispatcher会对请求的url进行解析,并判断应该将请求发送到应用程序的哪个部分,以及如何解析这一请求。在寻找到正确的controller与action之后,就可以通过该action来处理请求。action可以查阅请求中携带的数据,可以与模型交互,也可以调用别的action。最后,action会为视图准备充分的信息,视图则将所需的信息展现给用户。因为在形式上能够保持一致性,框架就变得简单,参与的各个组件能够做到各司其职,职责清晰,意图明确。

  这种形式的一致性基于一种朴素的思想,就是有限的约束比完全的开放更容易实施和遵循。正如社会总要有一套被人广泛认同的规则,来约束每位公民的行为,否则整个世界就会乱了套。形式一致性的本质在于概念的完整性,而它的基础则在于约定。架构的复杂性在于我们无法为混沌的系统做出正确的决策,如果能够为我们所要解决的场景抽象出整体的概念,就能够最大程度地对模型进行简化,从而给出一致性的约定。Rails利用现有的MVC模式,通过约定与惯例在形式上的一致性,实现了Web架构的简化。如果我们观察Maven的目录结构,就会发现Maven在针对依赖管理这样一个场景,同样提出了自己的概念模型,并在形式上规定了架构的一致性:

  简洁的架构常常能体现一种独到的美,而具有一致性的架构则会给软件系统带来和谐与平衡。架构风格的一致可以保证系统架构的统一,设计人员只要抓住了架构的风格特征,就能够把握这个系统的“神”,从而促进对系统架构的理解。对架构风格的重视,还有利于架构级别的资源重用,通过对问题域的分析,判断它应该属于哪一种架构风格的分类,就能够找到适合架构的原则、模式或现有的平台和框架。解决方案的一致性可以避免混乱的软件架构,促进团队成员之间的交流与协作,规避因为解决方案不一致而导致的资源浪费。保持一致的解决方案还可以保障软件质量,因为很多潜在的隐患与缺陷,恰恰都是因为不同的解决方案带来的冲突导致的。形式的一致是简化的前提,并能够保证系统概念的统一,通过抽象简化概念模型,并制订一致的架构约定,就能够简化整个架构体系,降低实现的难度。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注