前言
上一篇我们讲了 DDD 分层架构。但实际上,不只有一种分层方式。
业内常见的有三种:
- DDD 分层架构(上一篇讲的)
- 整洁架构(Clean Architecture,又叫洋葱架构)
- 六边形架构(Hexagonal Architecture,又叫端口-适配器架构)
它们看起来形态各异,但内核是一致的。这一篇我们一起看看。
一、整洁架构(洋葱架构)
整洁架构的提出者叫 Robert C. Martin(Bob 大叔)。它的形状像洋葱:
┌─────────────────────────────────────┐
│ Frameworks & Drivers (框架) │ ← 最外层
│ ┌─────────────────────────────┐ │
│ │ Interface Adapters (接口适配) │ │ ← 适配层
│ │ ┌─────────────────────┐ │ │
│ │ │ Use Cases (用例) │ │ │ ← 应用层
│ │ │ ┌─────────────┐ │ │ │
│ │ │ │ Entities (实体) │ │ │ │ ← 领域层
│ │ │ └─────────────┘ │ │ │
│ │ └─────────────────────┘ │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘
核心原则:依赖只能从外向内。内圈不依赖外圈。
各层职责:
| 层 | 职责 |
|---|---|
| Entities | 核心业务对象,封装企业级业务规则 |
| Use Cases | 应用业务规则,编排实体完成用例 |
| Interface Adapters | 适配器,转换数据格式(Controller、Repository 实现) |
| Frameworks & Drivers | 框架和外部工具(Web 框架、数据库、消息中间件) |
洋葱架构的核心思想:
- 越往内越稳定——内圈代码不依赖任何外部细节。
- 越往外越易变——外圈代码是技术实现,频繁更换不影响核心。
- 业务核心在中心——领域实体是软件的核心,不应该被任何外部依赖污染。
二、六边形架构(端口-适配器架构)
六边形架构的提出者是 Alistair Cockburn。它的形状是六边形(只是画法不同,内核一样):
┌──────────┐
│ Port 1 │ ← 端口:业务定义
└──────────┘
│
┌──────────┐ │ ┌──────────┐
│ Port 2 ├──┼──┤ Port 3 │ ← 多个端口对应外部世界
└──────────┘ │ └──────────┘
│
┌──────────┴──────────┐
│ Application Core │ ← 应用核心(业务逻辑)
└─────────────────────┘
核心概念:
- 端口(Port):业务核心对外暴露的接口(业务想要什么)。
- 适配器(Adapter):端口的具体实现(外部世界怎么提供)。
一个端口可以对应多个适配器。比如:
- “持久化端口”:MySQL 适配器、PostgreSQL 适配器、文件适配器。
- “消息端口”:Kafka 适配器、RabbitMQ 适配器。
业务核心不关心外部怎么实现,只关心接口签名。
三、三种架构的对比
虽然形态不同,三种架构的内核完全一致:
| 维度 | DDD 分层 | 整洁架构 | 六边形架构 |
|---|---|---|---|
| 中心 | 领域层 | Entities | Application Core |
| 依赖方向 | 自上而下 | 由外向内 | 通过端口-适配器隔离 |
| 业务位置 | 领域层 | 内圈 | 中心 |
| 外部适配 | 基础层 | Interface Adapters | Adapters |
| 关键差异 | 强调“层” | 强调“依赖规则” | 强调“端口-适配器” |
它们本质上是同一种思想的三个表达:
- DDD 分层用层的概念表达
- 整洁架构用依赖规则表达
- 六边形架构用端口-适配器表达
四、共性:三种架构都强调“内外分离”
无论是哪种架构,核心都做了同一件事:把易变的外部和稳定的业务核心分开。
外圈(易变)
↑
┌───────┴───────┐
│ 内圈(稳定) │ ← 业务核心
│ · 实体 │
│ · 领域服务 │
│ · 业务规则 │
└───────────────┘
为什么这件事如此重要?
因为企业级系统的需求变化规律是:
- 业务核心稳定(10 年不变)——选课的核心规则还是“学员选课、付钱、获得学习权限”。
- 外部技术易变(1-3 年变)——数据库从 MySQL 换 TiDB、消息从 Kafka 换 RocketMQ、HTTP 换 gRPC。
如果业务核心和外部技术耦合:
- 换数据库要重写业务代码(灾难)。
- 业务代码里到处是 SQL、Redis、HTTP 调用,没法测、没法改。
如果业务核心和外部技术分离:
- 换数据库只换 Repository 实现。
- 业务代码纯 Java,单测覆盖率高。
- 换前端框架、改 API 协议,业务不动。
五、怎么选?
我的个人建议:
| 项目类型 | 推荐架构 |
|---|---|
| 新项目、复杂业务 | DDD 分层(最主流,团队认知成本低) |
| 强调测试覆盖 | 整洁架构(依赖规则清晰,便于单测) |
| 强调可替换性(多数据库、多前端) | 六边形架构(端口-适配器最显式) |
| 团队小、业务简单 | 别用 DDD(过度设计) |
实际项目里,我几乎都用 DDD 分层。原因:
- 团队认知成本最低(提到“应用层”大家都知道是什么)。
- 招聘时容易招到懂 DDD 的人。
- 配套生态成熟(Spring、Spring Boot、Axon 框架等都支持)。
六、整洁架构的项目结构
如果选整洁架构,代码组织可能是这样:
com.example.course
├── domain # 实体
│ ├── Course.java
│ └── ...
├── usecase # 用例
│ ├── CreateCourseUseCase.java
│ └── PublishCourseUseCase.java
├── adapter # 适配器
│ ├── in # 输入适配器(Controller)
│ │ └── CourseController.java
│ └── out # 输出适配器(Repository、Message)
│ ├── CourseRepositoryImpl.java
│ └── EventPublisherImpl.java
└── framework # 框架配置
└── SpringConfig.java
七、六边形架构的项目结构
com.example.course
├── domain # 领域核心
│ ├── Course.java
│ ├── port # 端口(接口)
│ │ ├── in # 输入端口(用例接口)
│ │ │ └── CreateCourseUseCase.java
│ │ └── out # 输出端口(仓储接口)
│ │ └── CourseRepository.java
│ └── service # 业务核心实现
│ └── CreateCourseService.java
├── adapter # 适配器
│ ├── in # 输入适配器
│ │ └── CourseController.java
│ └── out # 输出适配器
│ ├── CourseRepositoryImpl.java
│ └── KafkaEventPublisher.java
└── application # 启动类
└── CourseApplication.java
注意端口的方向:
port/in:外部调用业务的接口(用例)。port/out:业务调用外部的接口(持久化、消息)。
八、整洁架构 vs 六边形 vs DDD 分层的实际差异
我用一个表格说清楚到底有什么不一样:
| 场景 | DDD 分层 | 整洁架构 | 六边形 |
|---|---|---|---|
| 业务代码位置 | domain/ | domain/ + usecase/ | domain/ |
| 仓储接口位置 | domain/repository/ | domain/port/out/ | domain/port/out/ |
| 仓储实现位置 | infrastructure/ | adapter/out/ | adapter/out/ |
| 应用服务位置 | application/service/ | usecase/ | domain/service/(实现 port/in) |
| 外部替换 | 改实现类 | 改适配器 | 改适配器 |
真正的区别在于命名和组织。业务内核完全一致。
九、我在线上做的一个案例
我们之前做一个在线教育平台,架构选型是 DDD 分层,但借鉴了六边形架构的端口-适配器思想:
- 仓储接口在领域层(DDD 分层)。
- 消息发布器接口在领域层(端口)。
- WebSocket 推送作为适配器,实现在基础层。
- Kafka 消息订阅作为适配器,实现在基础层。
这样业务核心不关心消息怎么发、WebSocket 怎么推。换消息中间件只换适配器。
这就是三种架构融合使用的典型场景。
十、决策树
你的项目需要什么?
├── 团队小、业务简单
│ └── 别用 DDD,三层架构足够
│
├── 业务复杂、团队中等
│ └── DDD 分层 + 依赖倒置(推荐)
│
├── 业务复杂、要支持多前端/多数据库
│ └── DDD 分层 + 端口-适配器(六边形思想)
│
└── 业务极其复杂、强调可测试
└── 整洁架构(洋葱)
总结
这一篇我们讲了:
- 三种架构的形态不同,内核一致——都是“业务核心稳定、外部易变”的分离。
- DDD 分层最主流,整洁架构最讲究依赖规则,六边形最显式端口-适配器。
- 实际项目可以融合使用——DDD 分层 + 六边形端口-适配器是常见组合。
- 架构选型要看团队认知——别选团队完全不懂的架构。
下一篇我们讲中台——在线教育平台的“通用能力”到底应该共享什么。