最常见的架构模式是分层架构模式,也被称为 N 层架构模式。这种模式是大多数 Java EE 应用程序的事实标准,因此被大多数架构师、设计师和开发人员广泛了解。分层架构模式与大多数公司的传统IT通信和组织结构密切相关,使其成为大多数商业应用开发工作的自然选择。
模式描述
分层架构模式中的组件被组织成水平层,每一层在应用程序中执行特定的角色(例如,表现逻辑或业务逻辑)。尽管分层体系结构模式没有规定模式中必须存在的层的数量和类型,但大多数分层体系结构包括四个标准层:表现层、业务层、持久层和数据库(图 1-1)。在某些情况下,业务层和持久化层被合并成一个业务层,特别是当持久化逻辑(如 SQL 或 HSQL)被嵌入到业务层组件中时。因此,较小的应用程序可能只有三个层,而较大和较复杂的商业应用程序可能包含五个或更多的层。
分层架构模式的每一层在应用程序中都有特定的角色和责任。例如,表现层将负责处理所有的用户界面和 1 个浏览器的通信逻辑,而业务层将负责执行与请求相关的特定业务规则。架构中的每一层都围绕着满足特定业务请求所需的工作形成了一个抽象。例如,表现层不需要知道或担心如何获得客户数据;它只需要在屏幕上以特定格式显示这些信息。同样,业务层也不需要关心如何将客户数据格式化以便在屏幕上显示,甚至不需要关心客户数据来自哪里;它只需要从持久化层获得数据,对数据执行业务逻辑(例如,计算值或聚合数据),并将该信息传递给预发送层。
图 1-1. 分层架构模式
分层架构模式的一个强大的特点是组件之间的关注点分离。一个特定层中的组件只处理与该层有关的逻辑。例如,表现层的组件只处理预发送逻辑,而驻扎在业务层的组件只处理业务逻辑。这种类型的组件分类使得在你的架构中建立有效的角色和责任模型变得很容易,而且由于定义明确的组件接口和有限的组件范围,使用这种架构模式开发、测试、治理和维护应用程序也变得很容易。
关键概念
注意在 图 1-2 中,架构中的每个层都被标记为封闭的。这是分层架构模式中一个非常重要的概念。一个封闭的层意味着当一个请求从一个层移动到另一个层时,它必须通过它下面的层才能到达下一个层。例如,一个来自表现层的请求必须首先经过业务层,然后到持久层,最后才到数据库层。
图 1-2. 封闭层和请求访问
那么为什么不允许表现层直接访问持久层或数据库层呢?毕竟,从表现层直接访问数据库要比通过一堆不必要的层来检索或保存数据库信息要快得多。这个问题的答案在于一个被称为隔离层的关键概念。
隔离层的概念意味着在架构的某一层所做的改变通常不会影响到其他层的组件:改变被隔离到该层的组件,可能还有另一个相关的层(如包含 SQL 的持久层)。如果你允许表现层直接访问持久层,那么对持久层中的 SQL 所做的改变就会影响到业务层和预发层,从而产生一个非常紧密耦合的应用程序,组件之间有很多相互依赖的关系。这样的架构就很难改变,也很昂贵。
隔离层的概念也意味着每一层都是独立于其他层的,因此对架构中其他层的内部运作几乎一无所知。为了理解这个概念的力量和重要性,考虑一个大型的重构工作,将表现框架从 JSP(Java Server Pages)转换成 JSF(Java Server Faces)。假设在表现层和业务层之间使用的契约(如模型)保持不变,那么业务层就不会受到重构的影响,并且完全独立于表现层使用的用户界面框架的类型。
虽然封闭的层有利于隔离层,因此有助于隔离架构内的变化,但有时某些层的开放是有意义的。例如,假设你想给一个架构添加一个共享服务层,其中包含由业务层内的组件访问的通用服务组件(例如,数据和字符串实用类或审计和日志类)。在这种情况下,创建一个服务层通常是个好主意,因为从架构上看,它将对共享服务的访问限制在业务层(而不是表现层)。如果没有一个单独的层,在架构上就没有任何东西可以限制表现层访问这些共享服务,从而使这种访问限制难以管理。
在这个例子中,新的服务层很可能位于业务层的下面,以表明这个服务层的组件不能从表现层访问。然而,这带来了一个问题,即业务层现在需要通过服务层才能到达持久层,这完全没有意义。这是分层结构的一个老问题,可以通过在结构中创建开放层来解决。
如 图 1-3 所示,本例中的服务层被标记为开放层,这意味着请求被允许绕过这个开放层而直接进入它下面的层。在下面的例子中,由于服务层是开放的,业务层现在被允许绕过它而直接进入持久层,这就非常合理了。
图 1-3. 开放层和请求流
利用开放层和封闭层的概念有助于定义架构层和请求流之间的关系,也为设计者和开发者提供了必要的信息,以了解架构中的各种层访问限制。如果不能记录或正确沟通架构中哪些层是开放的和封闭的(以及为什么),通常会导致紧耦合和脆性的架构,这些架构很难测试、维护和部署。
模式示例
为了说明分层结构是如何工作的,考虑一个来自商业用户的请求,以检索一个特定个人的客户信息,如 图 1-4 中所示。黑色箭头表示请求流向数据库以检索客户数据,红色箭头表示响应流向屏幕以显示数据。在这个例子中,客户信息包括客户数据和订单数据(客户下的订单)。
客户屏幕负责接受请求和播放客户信息。它不知道数据在哪里,如何检索,或者必须查询多少个数据库表才能得到数据。一旦客户屏幕收到获取某个特定个人的客户信息的请求,它就会将该请求转发给客户委托模块。这个模块负责了解业务层中的哪些模块可以处理该请求,以及如何到达该模块和它需要什么数据(合同)。业务层中的客户对象负责汇总业务请求所需的所有信息(在本例中是为了获取客户信息)。这个模块调用持久层中的 Customer dao(数据访问对象)模块来获取客户数据,同时也调用 Order dao 模块来获取订单信息。这些模块依次执行 SQL 语句来获取相应的数据,并将其传回给业务层的客户对象。一旦客户对象接收到数据,它就会汇总数据,并将信息传递给客户委托,然后将数据传递给客户屏幕,以呈现给用户。
图 1-4. 分层模式示例
从技术角度来看,这些模块可以有几十种实现方式。例如,在 Java 平台中,客户屏幕可以是一个 Java Server Faces(JSF),并将客户委托作为托管 Bean 的组成部分。业务层中的客户对象可以是一个本地的 Spring Bean 或一个远程的 EJB3 Bean。前面的例子中说明的数据访问对象可以实现为简单的 POJO(Plain Old Java Objects)、MyBatis XML Mapper 文件,甚至是封装了原始 JDBC 调用或 Hibernate 查询的对象。从 Microsoft 平台的角度来看,客户屏幕可以是一个 ASP(Active server pages)模块,使用 .NET 框架来访问业务层的 C# 模块,客户和订单数据访问模块被实现为 ADO(ActiveX 数据对象)。
一些思考
分层架构模式是一种可靠的通用模式,使其成为大多数应用程序的良好起点,特别是当你不确定什么架构模式最适合你的应用程序时。然而,在选择这种模式时,从架构的角度来看,有几件事情需要考虑。
首先要注意的是所谓的架构下沉洞反模式。这种反模式描述了这样一种情况:请求流经架构的多个层,作为简单的传递处理,在每一层内很少或没有执行逻辑。例如,假设表现层响应了用户检索客户数据的请求。表现层将请求传递给业务层,业务层将请求传递给持久层,持久层将对数据库层进行简单的 SQL 调用以检索客户数据。然后,数据被一路传回堆栈,没有额外的处理或逻辑来聚合、计算或转换数据。
每一个分层架构至少会有一些属于架构天坑反模式的场景。然而,关键是要分析属于这一类别的请求的百分比。80-20 法则通常是一个很好的做法,可以用来确定你是否正在经历架构下沉孔反模式。典型的情况是,大约 20% 的请求是简单的穿透处理,80% 的请求有一些与请求相关的业务逻辑。然而,如果你发现这个比例是相反的,大部分的请求都是简单的直通处理,你可能要考虑将一些架构层开放,要记住,由于缺乏层的隔离,控制变化会更加困难。
分层架构模式的另一个考虑是,它倾向于单体应用,即使你把表现层和业务层分成独立的可部署单元。虽然这对某些应用程序来说可能不是一个问题,但它确实在部署、总体健壮性和可靠性、性能和可扩展性方面带来了一些潜在的问题。
模式分析
下表包含了对分层架构模式的常见架构特征的评级和分析。每个特征的评级是基于该特征作为一种能力的自然趋势,基于该模式的典型实现,以及该模式的普遍知名度。关于该模式与本报告中其他模式的关系的侧面比较,请参考本报告末尾的 附录 A。
整体敏捷性评级:低
整体敏捷性是指对不断变化的环境做出快速反应的能力。虽然变化可以通过这种模式的层层隔离功能进行隔离,但由于大多数实现的单体性质以及这种模式下通常发现的组件的紧密耦合,在这种架构模式下进行变化仍然很麻烦,很费时间。
部署便利性评级:低
根据你实现这种模式的方式,部署可能成为一个问题,特别是对于大型的应用。一个组件的微小变化可能需要重新部署整个应用(或应用的大部分),导致部署需要在非工作时间或周末进行计划、安排和执行。因此,这种模式不容易成为连续的交付管道,进一步降低了部署的总体评级。
可测试性等级:高
由于组件属于架构中的特定层,其他层可以被模拟或存根,使得这种模式相对容易测试。开发者可以模拟一个演示组件或屏幕来隔离业务组件内的测试,也可以模拟业务层来测试某些屏幕功能。
性能评级:低
虽然一些分层架构确实可以很好地实现,但由于必须通过多层架构来实现业务请求的低效率,这种模式并不适合高性能的应用。
可扩展性评级:低
由于这种模式的紧耦合和单体实现的趋势,使用这种架构模式构建的应用程序通常很难扩展。你可以通过将各层分割成独立的物理部署或将整个应用程序复制到多个节点来扩展分层架构,但总的来说,粒度太宽,使其扩展成本很高。
易于开发的评级:高
开发的便利性得到了一个相对较高的分数,主要是因为这种模式是非常有名的,而且实现起来并不复杂。因为大多数公司在开发应用程序时都是按层(演示、业务、数据库)分离技能,这种模式成为大多数商业应用开发的自然选择。一个公司的沟通和组织结构与它开发软件的方式之间的联系被概述为所谓的 “康威定律”。你可以在谷歌上搜索 “康威定律”,以获得更多关于这种迷人的关联性的信息。
