找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4061

积分

0

好友

533

主题
发表于 6 小时前 | 查看: 4| 回复: 0

什么是架构?架构的核心就是为软件的核心复杂度提供有效的管理及消减手段。其中软件的复杂度又分为本质复杂度和偶然复杂度。关于复杂度的概念介绍,请看我之前的一篇架构文章。

软件架构到底是什么?架构都要做哪些事情?

本质复杂度就是软件系统映射的业务功能原生的复杂度,也就是业务核心复杂度。本质复杂度无法降低,只能去有效的管理,那么 DDD 领域驱动设计中领域、限界上下文等核心思想就是解决本质复杂度的。

偶然复杂度是指软件业务功能之外的复杂度,包括为了实现业务功能引入的软件工具、语言、构件及软件架构的组织形式等。因此偶然复杂度又分为工具性偶然复杂度和结构性偶然复杂度。偶然复杂度通过更先进的工具和更优的结构组织可以消减,但永远不能完全消除。

其中 DDD 分层架构(4层)设计,就是管理和消减结构性偶然复杂度的。那么引入 DDD 分层架构,就是最优秀的架构模式吗?显然答案是否定的,架构是不断演进的,永远都有更优的分层模式。比如我们常见的六边形架构(端口和适配器架构)、洋葱架构、整洁架构及清晰架构等。本文我们主要将 DDD 四层分层架构到整洁架构的演化。

01 传统的 DDD 分层架构

DDD传统四层架构图

用户接口层:用户接口层负责向用户显示信息和解释用户指令。这里的用户可能是:用户、程序、自动化测试和批处理脚本等等。

应用层:应用层是很薄的一层,理论上不应该有业务规则或逻辑,主要面向用例和流程相关的操作。但应用层又位于领域层之上,因为领域层包含多个聚合,所以它可以协调多个聚合的服务和领域对象完成服务编排和组合,协作完成业务操作。

领域层:领域层的作用是实现企业核心业务逻辑,通过各种校验手段保证业务的正确性。领域层主要体现领域模型的业务能力,它用来表达业务概念、业务状态和业务规则。

领域层包含聚合根、实体、值对象、领域服务等领域模型中的领域对象。

领域模型的业务逻辑主要是由实体和领域服务来实现的,其中实体会采用充血模型来实现所有与之相关的业务功能。其次,你要知道,实体和领域对象在实现业务逻辑上不是同级的,当领域中的某些功能,单一实体(或者值对象)不能实现时,领域服务就会出马,它可以组合聚合内的多个实体(或者值对象),实现复杂的业务逻辑。

基础层:基础层是贯穿所有层的,它的作用就是为其它各层提供通用的技术和基础服务,包括第三方工具、驱动、消息中间件、网关、文件、缓存以及数据库等。比较常见的功能是提供数据库持久化。

02 改良四层架构

DDD 分层架构的原则

分层架构的一个重要原则是每层只能与位于其下方的层发生耦合。分层架构根据每层耦合的紧密程度分为两种:

  • 严格分层架构:某层只能与位于其直接下方的层发生耦合。
  • 松散分层架构:则允许某层与它的任意下方层发生耦合。

传统的 DDD 分层架构,用户接口层、领域层和应用层均可与基础设施层发生耦合,属于松散分层架构。

这种模式在实际复杂项目中是有问题的, 将基础设施层放在最底层存在缺点,比如此时领域层中直接调用了一些技术实现,违背分层架构的基本原则,难以编写测试用例等。

何解?使用 依赖反转设计原则,具体依赖于抽象,而不是抽象依赖于具体实现各层对基础资源的解耦。低层服务(如基础设施层)应依赖高层组件(比如用户界面层、应用层和领域层)所提供的接口。

依赖反转后的分层方式:基础设施层在最上方,可实现所有其他层中定义的接口。

低层服务(如基础设施层)应依赖高层组件(比如用户接口层、应用层和领域层)所提供接口。高层定义好仓库的接口,基础设施层实现各层定义好的仓库接口。

优化后的 DDD 分层架构模型就属于 严格分层架构,任何层只能对位于其直接下方的层产生依赖。而传统的 DDD 分层架构则属于松散分层架构,它允许某层与其任意下方的层发生依赖。

依赖倒置后的DDD四层架构对比

那我们怎么选呢?综合我的经验,为了服务的可管理,我建议你采用严格分层架构。

在严格分层架构中,领域服务只能被应用服务调用,而应用服务只能被用户接口层调用,服务是逐层对外封装或组合的,依赖关系清晰。而在松散分层架构中,领域服务可以同时被应用层或用户接口层调用,服务的依赖关系比较复杂且难管理,甚至容易使核心业务逻辑外泄。

试想下,如果领域层中的某个服务发生了重大变更,那该 那该如何通知所有调用方同步调整和升级呢?但在严格分层架构中,你只需要逐层通知上层服务就可以了。

那么,采用严格分层架构后,DDD 代码结构如下图所示:

DDD四层架构代码结构手绘图

03 六边形架构

2005 年,Alistair Cockburn 提出了端口与适配器架构(Ports & Adapters Architecture),又称六边形架构(Hexagonal Architecture)。

架构演进:从分层到端口适配器

端口(Port)与适配器(Adapter)架构中的相关概念:

工具(Tools):
应用(application)层使用的工具,如 WebServer、CLI、DB、SearchEngine、MQ 等。

端口(Port):
在 application core 层定义的工具交互规范(即规定工具如何使用 application core,或 application core 如何被工具使用的契约);
对应编程语言中的 interface 定义;
端口的实现应符合应用层业务逻辑(不可简单模仿或直接调用底层工具的 API)。

适配器(Adapter):
连接工具(WebServer、CLI、DB、SearchEngine、MQ)与应用核心(application core)的代码单元。
适配器依赖端口(调用端口或实现端口),但 application core 仅依赖端口(不依赖具体适配器)。

主动适配器(Primary / Driving Adapters):

  • 组件:Controller(REST API)、CLI Handler、Message Listener。
  • 职责:接收外部输入,参数转换,主动调用应用核心(Application Core),触发应用执行某项活动。严禁包含业务规则。
  • 依赖关系:依赖 ServiceInterface(核心层定义的接口)。
    流程:Controller(主动适配器) -> 依赖 ServiceInterface(端口) -> 注入 ServiceImpl(端口的具体实现)

被动适配器(Secondary / Driven Adapters):

  • 职责:实现核心层定义的“被驱动端口”(如 UserRepository 接口),处理具体技术细节(SQL、网络调用)。被应用核心(Application Core)调用以执行外部动作,实现应用所定义的端口。
  • 组件:JpaUserRepositoryImpl、RedisCacheAdapter。
  • 依赖关系:依赖核心层接口,核心层不依赖它们。
  • 流程:端口 -> 被动适配器实现端口逻辑 -> 包装系统外部工具

示例:RepositoryInterface(端口)-> 被动适配器 MysqlRepositoryImpl 实现该接口 -> 内部调用 MySQL 数据库。

ServiceImpl 是核心业务逻辑的执行者,位于六边形内部(核心层);Controller 是外部入口的转换器,位于六边形外部(适配层)。两者通过核心层定义的接口解耦。

应用核心层(Application Core)

  • 组件:ServiceImpl(用例服务)、ServiceInterface(端口接口)、领域实体、领域服务。
  • 职责:ServiceImpl 实现业务流程协调,调用下游的“被驱动端口”(如 UserRepository 接口)。
  • 关键特征:完全独立于外部框架,可被单元测试直接覆盖,无 Spring/ASP.NET 等框架注解污染(纯 POJO/Plain Class)。

几边形不重要,可以 N 边;重点是 Port & Adapter 思想。关于端口与适配器架构的优势:

  • 核心业务逻辑位于最中心(最重要);
  • 核心业务仅依赖 Port;
  • Adapter 依赖 Port(主动适配器)或为 Port 提供具体实现(被动适配器);
  • 核心业务逻辑与实现细节(技术框架、底层存储、工具、传输/通信机制等)严格隔离;
  • 保持核心业务逻辑的可重用性与可测试性。

04 洋葱架构

洋葱架构(Onion Architecture)由 Jeffrey Palermo 于 2008 年提出。

洋葱架构示意图

洋葱架构(Onion Architecture)构建在端口与适配器架构(又称六边形架构)之上,在端口与适配器架构中仅提及两层:

  1. 外层——表示传输(通信)机制和基础设施(Infrastructure)
  2. 内层——业务逻辑(Business Logic)

而洋葱架构在 DDD(领域驱动设计)基础上,将内层(即业务逻辑层,Business Logic)进一步细分,最终形成以下层次:

  • Adapters(适配器层)——即六边形架构中的适配器(Adapter)层
    • User Interface(用户界面)、Infrastructure(基础设施)、Tests(测试)
  • Application Core(应用核心层),即原六边形架构中心的业务逻辑层(Business Logic Layer)
    • Application Services(应用服务层)
    • Domain Services(领域服务层)
    • Domain Model(领域模型层)

且明确界定了依赖方向:

  • 外层依赖内层;
  • 内层不依赖外层;
  • 并且,在不破坏依赖方向的前提下,外层也可直接调用任一内层(不一定是相邻的下一层),例如参考 CQRS 中 Query 的实现方式(Application Services 直接调用 DAO)。

05 整洁架构(Clean Architecture)

2012 年,Robert C. Martin(又名 Uncle Bob)提出了整洁架构(Clean Architecture),该架构将六边形架构(Hexagonal Architecture)与洋葱架构(Onion Architecture)等整合为一种可实际落地的软件架构方案。

整洁架构分层示意图

与洋葱架构相比,整洁架构的调整如下:

  • Application Services 调整为 Use Cases(用例);
  • Domain ServicesDomain Model 合并/统一调整为 Entities(实体)。

整洁架构并未引入突破性的新概念或新模式,但其价值在于:

  • 挖掘并重新强调了某些在实践中被忽视的重要概念、规则与模式;
  • 澄清了一些实用且关键的概念、规则与模式;
  • 为我们提供了如何将各类概念、规则与模式系统性整合的方法,从而形成一套构建复杂应用并保障其可维护性的标准路径。

06 CQRS

CQRS 是“命令查询职责隔离”(Command Query Responsibility Segregation)的缩写。这是一种将读取数据(查询)和写入数据(命令)的操作分开的模式。通过分离这些关注点,CQRS 可提高可扩展性、可维护性和灵活性。在基于 CQRS 的系统中,通常涉及以下组件:

  • Command: 代表改变系统数据的操作。命令负责创建、更新或删除数据,需校验业务规则。
  • Query: 表示从系统检索数据的操作。查询负责读取数据而不修改数据。
  • Command Handler: 处理和执行命令,更改系统状态。
  • Query Handler: 通过从系统检索数据并以合适的格式返回数据来处理查询。

CQRS命令查询架构图

CQRS 与事件溯源的协同

  • 事件溯源:命令端通过事件序列持久化状态变更,为查询端提供重建历史的可能性。
  • 数据同步机制:通过事件总线(如 Kafka)将命令端事件传播到查询端,更新读模型。

CQRS 与事件溯源结合

CQRS与事件溯源的微服务架构图

07 清晰架构(Explicit Architecture)

2017 年,Herberto Graca 在其《软件架构编年史》系列文章中提出了清晰架构(Explicit Architecture),即对 DDD、六边形架构(Hexagonal)、洋葱架构(Onion)、整洁架构(Clean)以及 CQRS 等多种架构思想进行融合后的综合架构方案。

清晰架构示意图

  • 最中心的红色多边形 Application Core(应用核心)表示业务逻辑的实现,即 应用核心
    • 红色多边形的边界即表示 端口(Port),即应用核心的入口/出口定义。
  • Application Layer(应用层),包括:
    • 使用 Repository 查找一个或多个实体;
    • 让这些实体执行若干领域逻辑;
    • 再次使用 Repository 将这些实体持久化,以有效保存数据变更;
    • 触发应用事件(例如:发送邮件、调用第三方 API、发送消息队列消息等)。
    • Application Services(应用服务):负责业务用例的编排服务及其接口定义。应用服务的作用通常如下:
    • CQRS 命令/查询处理器
    • Event Listener(事件监听器)
    • Port(端口)定义,例如:ORM 接口 Repository、搜索引擎接口、消息接口等。
  • Domain Layer(领域层):该层包含数据与操作数据的逻辑,它们仅与领域自身相关,独立于调用这些逻辑的业务流程;完全独立,对应用层完全无感知。
    • Domain Services(领域服务):封装涉及多个实体(相同或不同类型)的领域逻辑,且领域服务之间可相互调用。
    • Domain Models(领域模型):位于架构的正中心,完全不依赖外部任何其他层次的领域模型。它涵盖了领域中各类概念性的业务对象,如实体(Entity)、值对象(Value Object)、聚合根(Aggregate Root),以及在领域建模中使用的其他对象(例如领域事件 Domain Events,可简单理解为消息)。
  • 红色多边形的外侧左半圆部分即为 主/主动适配器(用户界面 User Interface 实现)
    • 例如:Spring MVC 中的 Controller 实现
    • Command Query Bus 命令/查询总线
  • 红色多边形的外侧右半圆部分即为 次/被动适配器(基础设施 Infrastructure 实现)
    • 例如:通过数据持久化实现 MySQL、短信通知、MQ 消息通知、搜索引擎(如 Elasticsearch)等;
    • Event Bus(事件总线)
  • 依赖方向为 由外向内,且内层对(外部)外层一无所知(参见前文洋葱架构的说明)。
    采用 按组件分包(Package By Component) 方式,即整合传统“按层分包(Package By Layer)”与“按特性分包(Package By Feature)”两种策略。其中,“组件(Component)”可理解为:
    • 属于特定领域内的业务逻辑服务与数据访问逻辑的组合;
    • 或进一步理解为一个具体的业务领域模块,例如账单(Billing)、用户(User)、评论(Comment)、账户(Account)等。

在清晰架构(Explicit Architecture)中,可理解为:

  • 首先按 层次 进行分包:
    • 表现层(Presentation)
    • 业务核心层(Application Core)
    • 基础设施层(Infrastructure)
  • 然后,在每层次内部,再进一步按 特性(或功能模块)进行分包。

云栈社区的技术文档板块整理了丰富的架构设计白皮书与避坑指南,方便你随时查阅。
技术文档板块




上一篇:Claude Code暗藏后门,阿里宣布全面禁用Claude全家桶
下一篇:谁说未来不可预测?聊聊因果、量变和“模糊的正确”
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-7-4 06:44 , Processed in 0.850412 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表