应用架构模式之Clean Architecture、Hexagonal Architecture 和 Clean & Hexagonal Architectures
理论部分
Clean Architecture、Hexagonal Architecture 和 Clean & Hexagonal Architectures 都是现代软件开发中用于构建可维护、可扩展的应用架构模式。虽然它们看似不同,但实际上有很多相似之处,它们在设计理念和目标上是相互关联的。下面会详细解释这三种架构的理论设计:
1. Hexagonal Architecture(六边形架构)
-
起源:Hexagonal Architecture(也叫Ports and Adapters)由 Alistair Cockburn 提出。它的核心思想是将应用程序的业务逻辑与外部世界(如数据库、外部服务、用户界面等)解耦。
-
架构核心:
- 业务逻辑(中心):这部分只关注应用的核心业务,完全与外部世界解耦。
- 端口(Ports) :端口是业务逻辑暴露给外部世界的接口,类似于输入和输出的接口。
- 适配器(Adapters) :适配器将外部系统(如数据库、UI、网络)转换为适合业务逻辑的格式,或者将业务逻辑的输出转化为外部系统可理解的格式。
-
目标:Hexagonal Architecture的目标是保持应用核心的独立性,使其不依赖于任何外部框架或技术栈,从而增加系统的可测试性、可维护性和扩展性。
2. Clean Architecture(清洁架构)
-
起源:Clean Architecture 是由 Robert C. Martin(也叫 Uncle Bob)提出的。它强调保持应用程序的核心业务逻辑与外部依赖(如数据库、UI、框架等)的分离。
-
架构核心:
- 中心层:核心业务逻辑,包含实体(Entities)、用例(Use Cases)、接口(Interfaces)等。这部分完全独立于外部系统,不包含任何框架或数据库相关代码。
- 外部层:外部依赖(例如:数据库、Web 框架、UI 等),这些都通过接口与核心业务逻辑交互。
-
目标:与 Hexagonal Architecture 类似,Clean Architecture 旨在确保核心业务逻辑不受外部系统的影响,从而提高系统的可维护性和可测试性。它通过明确的分层来确保业务逻辑的独立性。
3. Clean & Hexagonal Architectures(Clean & Hexagonal)
-
结合了两者的优点:一些架构实践者将 Clean Architecture 和 Hexagonal Architecture 结合使用,形成一个更加灵活、可扩展的设计模式。这种架构同时借鉴了 Clean Architecture 的分层设计和 Hexagonal Architecture 的端口与适配器模式。
-
实现方式:
- 核心业务逻辑:与 Clean Architecture 类似,核心业务逻辑是应用程序的中心部分,与任何外部技术和框架解耦。
- 端口与适配器:引入 Hexagonal Architecture 中的端口(Ports)和适配器(Adapters),用于处理外部依赖与核心业务逻辑的交互。
- 双向接口:外部系统通过适配器与端口交互,核心业务通过端口暴露必要的接口。这种双向接口可以有效支持测试和替换外部依赖。
-
目标:通过结合 Clean Architecture 和 Hexagonal Architecture,开发者可以获得更强的模块化和灵活性,同时保留两者的优点,确保核心业务逻辑的独立性和外部世界的解耦。
关联和区别
| 架构类型 | 核心思想 | 主要目标 | 关联点 |
|---|---|---|---|
| Hexagonal Architecture | 将核心业务与外部系统解耦,使用端口和适配器来连接外部依赖。 | 业务逻辑与外部世界的解耦。 | 强调业务逻辑的独立性。 |
| Clean Architecture | 通过分层将业务逻辑从外部框架和技术栈中隔离,使用依赖反转原则。 | 保持业务逻辑的核心独立性。 | 强调分层设计和核心业务的独立性。 |
| Clean & Hexagonal | 结合了 Clean Architecture 和 Hexagonal Architecture 的优点,使用分层和端口适配器模式。 | 提供更灵活的架构,保持业务核心的独立性。 | 结合了 Clean Architecture 的分层和 Hexagonal Architecture 的端口与适配器模式。 |
总结
- Hexagonal Architecture 专注于通过端口和适配器模式使核心业务与外部世界解耦。
- Clean Architecture 通过分层设计(核心业务逻辑、用例、外部依赖等)来实现核心业务的独立性。
- Clean & Hexagonal 是将两者结合的产物,它结合了 Clean Architecture 的层次结构和 Hexagonal Architecture 的端口适配器模式,从而提供更灵活、更模块化的架构设计。
这些架构模式的共同目标是将核心业务逻辑与外部技术细节隔离,提高系统的可测试性、可维护性和可扩展性。
实践(通过设计一个订单服务来理解Clean & Hexagonal)
传统的MVC架构
MVC架构的基本结构:
-
UI Layer (用户界面层)
- 这个层处理用户的输入和展示输出。图中的“Rest Clients”可以代表前端应用或其他UI系统。
-
API Layer (API层)
- 这个层接收来自UI层的请求,并通过REST控制器将其转发到Domain层。它处理与用户交互的数据请求。
-
Domain Layer (业务逻辑层)
- 这是架构的核心,处理应用程序的业务逻辑。在订单服务的场景下,这一层会处理诸如验证订单、计算总价等业务规则。
-
Data Layer (数据层)
- 这个层处理数据的存储和访问。数据存储可以是关系型数据库(如MySQL)或其他存储系统。
1. 依赖方向
- MVC架构: 在MVC中,依赖的方向通常是自上而下的。例如,UI层依赖API层,API层依赖Domain层,Domain层依赖Data层。这使得较高层次的组件依赖于较低层次的实现,导致修改时较高层的变化可能会影响到较低层的实现。
2. 职责分离
- MVC架构: 每一层的职责较为明确,UI层负责展示,API层负责接收请求,Domain层负责处理业务逻辑,Data层负责数据存取。但由于较高层的代码依赖于较低层的实现,职责分离的程度可能不如Clean & Hexagonal架构那样彻底。
3. 可测试性
- MVC架构: 因为MVC的各个层次之间存在紧密的依赖关系,尤其是在业务逻辑层和数据层之间,单元测试可能需要模拟多个层次,增加了测试的复杂度。
4. 灵活性
- MVC架构: 虽然各层次分工明确,但由于较高层依赖于较低层,灵活性有所降低。尤其是UI层和API层的变化可能会影响到整个架构的调整。
MVC的限制: MVC架构通常存在以下限制:
- 依赖问题:在MVC架构中,较高层(如UI层)通常依赖于较低层(如API层或数据层)。这种依赖关系可能会导致应用变得难以扩展和维护,特别是当需要替换或升级某一层的实现时,可能会影响到其他层。
- 不易替换:例如,换用不同的数据库或外部服务时,往往需要修改多个层次的代码。
- 测试复杂:各个层次之间存在紧密的依赖关系,尤其是在业务逻辑层和数据层之间,单元测试可能需要模拟多个层次。
Clean & Hexagonal架构
变化概述:
- 中心化业务逻辑:在Clean架构中,核心的业务逻辑(Domain Layer)被从外部组件(如UI、数据库、外部服务)中独立出来,变成系统的核心。所有的外部变化(如更换数据库或UI框架)都不需要影响到核心的业务逻辑。
- 依赖反转(DIP) :应用采用依赖反转的原则,依赖方向从外部(UI层、数据库等)指向内部的业务逻辑层。具体来说,外部的组件(如API、数据库)通过“接口”与核心业务逻辑交互,而不是直接依赖于核心业务层的实现。
- 接口与适配器:Clean架构使用了“适配器”模式,外部组件通过适配器与系统核心进行交互。比如,API层和数据层不直接依赖于核心业务逻辑,而是通过接口(输入端口和输出端口)与业务逻辑进行连接。不同的适配器(如API适配器、消息适配器)可以轻松替换,系统的核心部分保持不变。
1.Domain层(业务逻辑)依赖注入(DI)
首先在domian定义Data Interface、External Interface、Message Interface输出端口,分别由数据库、外部服务、消息队列层实现(适配)。通过接口依赖注入(DI)的方式实现对数据库、外部服务、消息队列层的调用。domain层包含了所有的业务规则,它不依赖于任何外部组件(如数据库或外部服务)。业务逻辑和存储层之间的交互通过输出端口接口来实现。
2.数据库、外部服务、消息队列层(外部服务适配器)依赖反转(DIP)
数据层通过适配器与数据库进行交互,外部服务也可以通过适配器与业务逻辑进行交互。数据层的适配器实现了Domain层定义的接口,使得数据存储方式能够被轻松替换。
外部服务、消息队列通过独立的适配器与系统进行交互,这些适配器实现了业务逻辑所需的接口。
API层(主要适配器) :API层仍然处理HTTP请求,但它不再依赖于业务逻辑的具体实现,而是依赖于业务逻辑的接口。API层通过输入接口端口与业务逻辑进行交互。
总结:
- MVC架构中,系统的各层(UI、API、Domain、Data)有明确的层次依赖关系,API层通常依赖于Domain层,Domain层又依赖于数据层。这种结构虽然清晰,但当系统变得更复杂时,修改某一层时可能会影响到其他层,尤其是对数据存储的改变。
- 在Clean架构中,依赖关系被反转。核心的业务逻辑不再直接依赖于外部组件,所有外部服务(如数据库、消息队列、外部服务)都通过接口和适配器与业务逻辑交互。核心业务逻辑保持独立,易于测试和替换,使得系统具有更好的扩展性和灵活性。