本部分对常见架构进行简单的说明。
一、三层架构之经典 MVC
经典的 MVC 架构(Model-View-Controller)架构是软件系统架构设计中的经典,它将应用程序分为三个主要部分:
- 模型(Model)
- 视图(View)
- 控制器(Controller)
这样的三层架构有助于实现业务逻辑的分离,提高代码的可维护性和可扩展性。
二、 CQRS(命令查询职责分离)
命令查询职责分离模式(Command Query Responsibility Segregation,CQRS)是一种软件架构模式,它通过从业务上分离修改(Command:增、删、改,会对系统状态进行修改)和查询(Query:查,不会对系统状态进行修改)的行为,使逻辑清晰,提高系统的可维护性和灵活性。
1 、优点
- 业务逻辑清晰:将修改和查询操作分开,使业务逻辑更加清晰。命令负责修改状态,而查询负责获取信息,降低系统复杂性
- 可维护性增强:通过将写模型和读模型分离,可以独立演化和优化两者。提高了系统的可维护性,开发人员可以更容易地理解和修改两个模型
- 扩展性:支持根据系统需求独立扩展命令和查询部分,这种灵活性使得系统更容易适应不断变化的需求
- 更好的性能优化:通过专门优化读模型,可以更好地满足查询性能的需求,有助于提高系统的整体性能
2 、不足
- 结构复杂:引入两个独立的模型(读写),使得系统结构复杂度高,维护和理解这两个模型之间的关系可能需要更多的工作
- 冗余代码:由于存在独立的读写模型,可能导致代码冗余,特别是对于一些共享的业务逻辑,这需要谨慎的设计和维护,以避免冗余
- 性能问题:涉及多次数据库操作,可能导致性能问题,特别是在需要频繁进行读写操作的情况下,需要根据具体情况进行性能优化
- 工作流不直观:将任务分解为多个步骤,可能导致工作流难以理解,尤其是对于新加入的开发人员,文档和培训对于确保团队对工作流的理解至关重要
- 缺乏标准化:并没有广泛的标准,团队在实践中需要根据自身需求进行调整,可能缺乏一致性和规范性
3 、总结
CQRS 是一个特定场景下有益的架构模式,但在引入时需要仔细权衡其优缺点。在对系统的可维护性、可扩展性和性能有较高要求的情况下,CQRS 可能是一个有价值的选择。然而,对于简单的应用程序,引入 CQRS 可能会增加不必要的复杂性。在实践中,团队应该根据具体情况慎重考虑是否采用 CQRS 。
三、六边形架构
六边形架构(Hexagonal Architecture)也称为端口适配器模式,是 Alistair Cockburn 在 2005 年首次提出的一种软件架构模式。其主要目标是将应用程序的核心逻辑与特定的输入/输出技术解耦,使其能够更灵活的应对变化和演化。
1 、三个原则
明确分层层次(Explicitly separate Application,Domain,and Infrastructure)
在六边形架构中,明确分离应用程序(Application)、领域(Domain)和基础设施(Infrastructure)是关键的设计原则,这种分离使开发者将关注点分离开,每一层都可以独立变化而不影响其他层,提高了系统的可维护性、可测试性和可扩展性。
- 应用程序层(Application Layer)
-
- 责任:包含应用程序的业务逻辑,负责协调领域层和基础设施层的交互,不包含具体的业务规则,而是将请求委托给领域层进行处理
- 实现:通常由应用服务组成,提供用例的实现,负责接收外部请求、处理输入数据、协调领域层的操作,并调用适当的基础设施服务
- 领域层(Domain Layer)
-
- 责任:包含系统的核心业务逻辑和领域模型,这是系统的灵魂,包含了问题域的概念、规则和约束
- 实现:由实体(Entities)、值对象(Value Objects)、聚合(Aggregates)、领域服务(Domain Services)等构成,协调工作,实现业务规则,确保系统的行为符合领域的要求
- 基础设施层(Infrastructure Layer)
-
- 责任:负责处理与外部系统的通信、数据库访问、日志记录等与具体技术相关的事务,包含所有与技术实现有关的组件
- 实现 :包括数据库访问层、外部服务通信层、日志记录、配置等,这些组件对系统的业务逻辑是透明的,通过接口与应用程序层和领域层进行交互
依赖关系指向领域层(Dependencies are going from Application and infrastructure to the Domain)
确保领域层保持独立性,不依赖于具体的应用程序逻辑或基础设施技术,提高系统的可测试性、可维护性和可扩展性。
技术上,就是 Application 和 Infrastructure 都不能直接互相调用对方的功能或者操作,只有经过 Domain 的接口才能沟通,确保 Domain 部分具有完整的独立性,不受 Application 和 Infrastructure 变更的影响,从而保持稳定,这样的代码结构易读、易管理、易维护。
- 依赖关系的方向
-
- 应用程序层向领域层的依赖:应用程序层包含用例的实现,负责协调系统的操作并调用领域层的服务
- 基础设施层向领域层的依赖:基础设施层包含与技术实现相关的组件,领域层不应该直接依赖于这些具体的技术实现,而是通过接口定义领域层所需的服务,并由基础设施层来实现这些接口
- 实现方式
-
- 领域服务接口:在领域层中定义接口,描述领域服务的行为,这些接口是应用程序层和基础设施层依赖的抽象 ➡️ 如果领域层需要使用某种外部服务,可以在领域层定义一个接口,然后在基础设施层中实现这个接口
- 依赖反转:通过依赖反转原则,应用程序层和基础设施层不直接依赖于领域层的具体实现,而是依赖于领域层定义的接口和抽象,这会让领域层能独立于其他层进行演化,不受具体实现细节的影响
- 测试驱动开发:将依赖关系的方向设计成从应用程序和基础设施指向领域,有助于采用 TDD,通过在应用程序层和基础设施层编写测试用例,并通过模拟或替代领域层的具体实现,可以更容易进行单元测试,确保领域层的正确性
使用端口和适配器隔离边界(We isolate the boundaries by using Ports and Adapters)
六边形架构使用端口和适配器模式来隔离系统的边界,以提高系统的可测试性、可维护性和灵活性,目标是将业务逻辑与外部环境解耦,让系统的各个部分更容易替换、测试和理解
- 基本概念
-
- 端口:定义系统与外界通信的接口,表示系统的一种能力或服务,通常通过接口来表示,定义系统对外提供的服务,不关心具体的实现,只关注服务的契约
- 适配器:连接系统内部和外部的组件,负责将系统内的数据结构或接口转换成外部系统期望的形式,是外部环境和系统内部通信的桥梁,确保系统的核心业务逻辑不受业务环境的影响
- 具体应用
-
- 内部端口和适配器:将应用程序划分为核心业务逻辑(Application)和外部环境(Infrastructure)两部分,内部端口表示系统对外部环境提供的服务,而内部适配器则负责将内部数据结构适应外部环境的需求
- 外部端口和适配器:外部端口表示系统对外部环境的依赖,而外部适配器则将外部环境的服务适配成系统内部期望的形式,使系统的核心业务逻辑不直接依赖于具体的外部实现,而是通过端口和适配器与外部环境进行通信
2 、特点
- 对称性:系统的设计围绕着一个中心的应用核心组件,所有的交互都必须通过这个中心
- 隔离性:应用程序的核心组件被一层薄的适配器包围,使得它对外部环境的变化免疫
- 可插拔性:系统可以通过改变其适配器来更改接口,并适应不同的上下文
3 、组成部分
- 应用核心:整个架构的心脏,包含了应用程序的业务逻辑和领域模型,不依赖任何特定的外部元素,只关注应用程序的核心功能
- 端口:是应用程序核心向外提供的接口,定义了应用程序如何与外部世界进行通信
- 适配器:用于连接应用程序核心和外部世界的栋梁,实现了端口的具体行为,并将请求路由到正确的位置
4 、好处
- 灵活性:当外部环境发生变化时,只需要修改适配器即可,而无需触及应用程序的核心
- 测试友好:可以轻松模拟和替换适配器,从而更容易测试应用程序的核心部分
- 低耦合:应用程序的核心部分与外部环境完全解耦,使其更易于维护和扩展
5 、不足
- 设计复杂度高
- 需要花费更多时间进行需求分析和设计
- 学习曲线比较陡峭,需要具备一定的软件设计知识
6 、总结
六边形架构是一种有助于提高代码代码质量和维护性的设计方式,但它也有一定的学习成本和实施难度。在实际应用中,可以根据具体项目的规模和复杂程度以及团队的技术能力,灵活运用此模式。
四、洋葱架构
洋葱架构(Onion Architecture)是由 2008 年,Jeffrey Palermo 提出,它在端口和适配器架构的基础上将领域放在应用中心,将用户用例和基础设施放在外围,这种设计思路类似于六边形架构,都是通过适配器代码将应用核心从对基础设施的关注中解放出来,以避免基础设施代码渗透到应用核心之中,使得框架和中间件需要改变替换的时候更加轻松,不会影响到核心领域。
1 、设计原则
- 依赖性:洋葱架构中的圆圈代表不同的责任层,进入越深越接近领域和业务规则,外圈代表机制,内圈代表核心领域逻辑,外层依赖于内层,而内层对外圈一无所知
- 数据封装:每个圈层封装内部实现细节,向外层公开接口,在内层定义抽象接口,在外层提供具体实现,确保专注于领域模型,而不必过多地担心实现细节。运行时,可以使用依赖注入框架,将接口和实现连接起来
- 关注点分离:应用系统被分为多个层级,每一个层级都有一组职责,并解决不同的关注点
- 耦合性:低耦合性,一个模块和另一个模块交互时不需要关注另一个模块的内部具体实现,所有的内部层级不需要关注外部层级的具体实现
2 、架构分层
- 应用服务层:负责协调请求步骤的服务,不应该有任何业务逻辑,应用服务与其他服务交互来满足客户请求
- 领域服务层:负责保持领域逻辑和业务规则,所有的业务逻辑应该作为领域服务的一部分来实现。领域服务由应用服务协调,服务于业务用例。不是简单的 CRUD 服务
- 领域模型层:封装属性和实体行为
- 基础设施层:在洋葱架构中的最外层,负责与外部世界交互,不解决任何领域的问题,没有任何逻辑
- 可观察性服务层:负责监控应用,可以用于数据收集(指标、日志、痕迹)、数据存储、可视化等
3 、优势
洋葱架构建立在一个领域模型上,各层之间通过接口交互,其设计思想是在领域实体和业务规则构成架构的核心部分时,尽可能将外部依赖性保持在外:
- 提供灵活、可持续和可移植的架构
- 各层之间没有紧密耦合,且有关注点的分离
- 所有代码都依赖更深的层或者中心,提高可维护性
- 提高整体代码的可测试性,单元测试可以在单独的层创建,不会影响其他的模块
- 框架/技术可以容易改变而不影响核心领域
4 、不足
- 设计复杂性:多个端口和适配器需要额外的设计工作且增加了系统复杂度
- 维护困难:洋葱架构的系统非常复杂对应的维护难度也比较高
5 、总结
洋葱架构适用于大型、复杂的应用程序,对于需要长期维护和演进的系统,其设计理念有助于降低变更的风险,提高系统的可靠性和可扩展性,在具体应用时,可以根据项目规模和团队技术水平做出适当的调整。