概述
前面聊过了应用架构风格的演进,其中提到的分层架构风格,其实现先后经历了MVC架构、三层架构、DDD架构、六边形架构等模式,那么,今天就聊一聊分层架构的演进。
在今天看来,分层架构是运用最为广泛的架构模式。首先,几乎每个软件系统都通过层(Layer)来隔离不同的关注点,在层中应对不同需求的变化,使得这种变化可以独立演进。此外,软件系统架构要考虑技术复杂性与业务复杂性的分离,通常引起技术实现发生变化的原因与引起业务发生变化的原因显然不同,而分层架构模式是隔离技术复杂度的利器。
将一个软件系统进行分层设计,对每个开发人员来说,已然成了很自然的事情,都不需要问为什么了,而且很多人都能信手拈来很多架构模式,如经典的三层架构、DDD的四层架构、六边形架构、洋葱圈架构、整洁架构。
但细细想来,为啥有这么多的模式,它们之间有什么联系吗?
架构模式演进
首先我们先看一下分层架构的演进,学习一下这些架构模式的分层关注点,以及层之间的依赖关系,然后尝试总结和定义分层原则。
MVC架构
2000年之前,软件应用架构多采用MVC架构。MVC模式(Model View Controller)模型-视图-控制器模式,是将数据模型与界面显示分离的一种架构模式。MCV模式最大的优势所在,就是将视图和模型分离,大大提高了代码的可重用性(多个视图可以共享一个模型)。
三层架构
2003年之前,主流架构模式是三层架构。经典三层架构为了符合“高内聚,低耦合”思想,自顶向下由用户界面层(User Interface Layer)、业务逻辑层(Business Logic Layer)与数据访问层(Data Access Layer)组成。层与层之间的依赖是向下的,上层对下层的调用,是通过接口实现的,而真正提供服务的是下层的接口实现类。它有效地隔离了业务逻辑与数据访问逻辑,使得这两个不同关注点能够相对自由和独立地演化。
DDD分层架构
2003年,Eric Evans在其著作《领域驱动设计》中提出了DDD分层架构,旨在解决传统三层架构中业务逻辑层难以维护和演进的问题。DDD的核心在于对业务领域的深入建模和精准划分,并通过分层架构的方式实现代码的清晰组织。在DDD分层架构中,业务逻辑层得到了进一步的丰富和深化,被细分为应用层和领域层,从而使得业务逻辑的处理更加专业和高效。
此外,DDD还将传统三层架构中的数据访问层更名为基础设施层,这一变革不仅突破了之前数据库管理系统的限制,还大大扩展了基础设施层的内涵。基础设施层现在负责封装技术复杂度,为上层提供统一的接口和封装,使得整个系统更加灵活和可扩展。
通过DDD分层架构,开发人员能够更好地关注业务领域本身,降低技术实现的复杂度,提高软件的质量和可维护性。同时,这种架构模式也使得系统的演进变得更加平滑和可控,为企业的长远发展奠定了坚实的基础。
- 接口层:和三层架构第一层基本保持不变。
- 应用层:从三层架构里的业务逻辑层剥离出的应用服务,应用服务负责接收外部请求,协调领域对象(聚合和领域服务),以及处理事务和安全性等方面的问题。应用服务通常用于暴露接口给外部系统(例如用户界面、API等)。
- 领域层:从三层架构里的业务逻辑层剥离出的核心业务逻辑,也即领域模型(聚合、聚合根、实体、值对象、领域服务)所在层。聚合是一个边界内的领域对象的集合,是领域逻辑的核心单元,其目的是将领域模型模块化。领域服务是一个面向领域的服务,负责处理特定的领域逻辑,通常被用于处理跨实体的复杂业务操作,领域服务通常不保存状态,而是执行一些操作或计算。
- 基础设施层:包含原三层架构的数据访问层,也包括从三层架构里的业务逻辑层剥离出的技术框架、中间件系统、以及依赖其它应用系统的相关代码。
六边形架构
2005年,Alistair Cockburn提出的六边形架构将系统巧妙地划分为内部和外部两大部分。内部聚焦于核心的业务逻辑,而外部则负责业务逻辑的入口调用和出口依赖的管理。内部与外部之间的通信是通过精心设计的端口来实现的,这些端口实质上代表了一种协议,通常以接口API的形式呈现。
为了确保不同形态的外部系统能够顺畅地访问内部业务逻辑,或者在内部业务逻辑需要依赖外部系统时能够无缝集成,六边形架构引入了适配器的概念。适配器作为桥梁,使得内部与外部之间的交互变得灵活而可控。因此,六边形架构也常被称为端口适配器架构。
通过这种架构模式,六边形架构不仅增强了系统的可扩展性和可维护性,还使得业务逻辑与外部系统的解耦成为可能,为复杂系统的设计和开发提供了一种高效且可靠的解决方案。
六边形架构主要关注如何将应用程序的核心业务逻辑与外部系统解耦,使其更加灵活、可扩展和可维护。
然而,六边形架构本身并不涉及内部业务逻辑的具体实现细节。内部业务逻辑的实现需要依赖于深入的业务领域知识和适当的技术选型。在这方面,领域驱动设计(DDD)为我们提供了有力的指导。
因此,在实际应用中,我们可以结合六边形架构和DDD的最佳实践,将六边形架构的内部业务逻辑进一步细化为DDD的应用层和领域层。应用层负责处理用户的输入和输出,协调领域层的对象来执行相应的业务操作;而领域层则专注于业务领域的核心概念和规则,确保业务逻辑的完整性和一致性。通过这种结合,我们可以充分利用六边形架构和DDD的优势,构建出既灵活又健壮的软件系统。
洋葱圈架构
2008年,Jeffrey Palermo巧妙地结合了六边形架构与DDD分层架构,提出了洋葱圈架构。这一架构与六边形架构有着诸多相似之处,但它在内部业务逻辑的划分上更为精细,增加了一个领域服务层。这种分层设计使得每层的依赖关系变得清晰明了,外层依赖于内层,构建了一个有序且高效的系统结构。
然而,这并不意味着在访问内部的领域模型时,必须严格经过应用服务层和领域服务层。实际上,对于一些没有涉及复杂业务逻辑的场景,我们并不需要特意构建领域服务层。在这种情况下,应用服务层可以直接与领域模型进行交互,从而简化了系统结构,提高了开发效率。
这种灵活性正是洋葱圈架构的魅力所在。它允许我们根据具体业务需求,灵活地调整系统结构,确保在满足功能需求的同时,保持系统的清晰度和可维护性。因此,在设计和实现洋葱圈架构时,我们应充分理解其核心理念,并根据实际情况进行合理的调整和优化。
整洁架构
2012年,Rob Martin提出了整洁架构,它与洋葱圈架构有着诸多相似之处。整洁架构的核心原则在于确保代码依赖关系始终从外向内流动,每一圈层都代表着软件系统的不同组成部分。随着圈层向内深入,抽象程度逐渐提高,外层侧重于实现具体的机制,而内层则聚焦于策略层面的设计和实现。
这种分层设计使得各层之间的依赖关系清晰明了,有效地实现了领域层与业务之外其他因素的隔离。通过整洁架构,我们可以确保领域层专注于核心业务逻辑的处理,而不受外部技术细节或特定实现方式的干扰。
整洁架构的引入,不仅提高了软件系统的可维护性和可扩展性,还使得开发人员能够更加专注于业务领域的深入探索和创新。因此,整洁架构已成为现代软件设计中备受推崇的一种架构模式,为构建高质量、可靠的软件系统提供了有力的支持。
架构原则和协作
分层架构越来越普及,而我们的架构却变得越来越笨拙,为什么会这样呢?其实就是因为大部分的软件设计师并未理解分层架构的本质,只是照葫芦画瓢简单地将分层模式应用到系统中。要么采用经典的三层架构,要么遵循领域驱动设计改进的四层架构,要么应用六边形架构,但这样的分层设计带来了什么,如此分层究竟有何道理却从未思考过。因此,这里我们提出2个基本问题,并尝试回答一下:
- 分层的依据与原则是什么?
- 层与层之间是怎样协作的?
分层的依据与原则
-
分层的第一个依据是:基于不同的关注点和不同的调用目的划分层次。 分层架构中的层次越往上,其抽象层次就越面向用户,面向业务;分层架构中的层次越往下,其抽象层次就变得越通用,面向技术和设备。以DDD分层架构为例,之所以引入应用层(Application Layer),就是为了给调用者提供完整的业务用例。
-
分层的第二个依据是:针对变化原因和频率划分层次。 分层时应针对不同的变化原因确定层次的边界,严禁层次之间互相干扰,或者至少将变化对各层带来的影响降到最低。例如数据库结构的修改自然会影响到基础设施层的数据模型以及领域层的领域模型,但当我们仅修改基础设施层的数据库访问实现逻辑时,就不应该影响到领域层了。
-
分层的第一个原则是:层与层之间的关系应该是正交的。 所谓“正交”,并非二者之间没有关系,而是垂直相交的两条直线。唯一相关的依赖点是这两条直线的相交点,即两层之间的协作点。正交的两条直线,无论哪条直线进行延伸,都不会对另一条直线产生任何影响(指直线的投影)。如果非正交,即“斜交”,当一条直线延伸时,它总是会投影到另一条直线,这就意味着另一条直线会受到它变化的影响。
-
分层的第二个原则是:保证同一层的组件处于同一个抽象层次。 该原则要求同一个抽象层的组件面向的能力是一致的,要么是面向用户、要么面向应用、要么面向业务、要么是面向技术设备,这样上层才能更好的组合下次的能力。
层与层之间的协作
-
上层依赖下层,外层依赖内层。 通过观察上面各种架构分层可以发现,分层架构的依赖大多都是自顶向下传递的,这也符合大多数人对分层的认知模型。从抽象层次看,层次越处于下端,就会变得越通用越公共,与具体的业务隔离得越远。出于重用的考虑,这些通用和公共的功能往往会被单独剥离出来形成平台或框架,在系统边界内的低层,除了面向高层提供足够的实现外,就都成了平台或框架的调用者。换言之,越是通用的层,越有可能与外部平台或框架形成强依赖。若依赖的传递方向仍然采用自顶向下,就会导致系统的业务对象也随之依赖于外部平台或框架。为什么是这样呢?我们通常理解的依赖,是指被依赖方需要为依赖方(调用方)提供功能支撑,而依赖的抽象是由下层定义的,这样的话,下层的实现变化就会传导到上层。
-
依赖倒置原则(Dependency Inversion Principle,DIP) 它要求“高层模块不应该依赖于低层模块,二者都应该依赖于抽象”。因此,依赖倒置原则隐含的本质是:高层要依赖不变或稳定的接口,通常这些抽象接口是由高层来定义的,而底层来实现这些稳定的接口。
总结
不管是三层架构、DDD分层架构、六边形架构、洋葱圈架构、还是整洁架构,都提倡以业务为核心,解耦外部依赖,分离业务复杂度和技术复杂度。
现在我们搞清楚了如何分层,也就能对上面的各种分层架构做一些思考了。那么上面的这些架构能否满足你的设计需要呢?如果不能,你是否可以设计自己的架构模式呢?