认识一下:与外部世界隔离的代码
\n\n您是否曾因为将数据库从PostgreSQL更换为MongoDB而重写业务逻辑?或者半夜惊醒,只因想到需要添加新的HTTP端点,而整个应用就是一个3000行的巨型main.go文件?
如果是——欢迎加入我们的行列。如果不是——要么您刚接触Go不久,要么您已经在使用六边形架构(也称为端口与适配器)。
\n\n本文将探讨如何构建一个不惧变化的Go应用。无论是更换数据库、从REST迁移到gRPC,还是引入新框架,都无需重写系统核心。只有代码。只有Go。没有废话。
\n\n通俗理解六边形架构
\n\n把您的应用想象成宇宙飞船。中心是驾驶舱(业务逻辑)。周围是对接端口。通过端口连接不同模块:引擎(数据库)、传感器(HTTP请求)、与地球的通信(消息队列)。
\n\n如果您决定更换引擎——只需断开旧的,连接新的。驾驶舱甚至不会察觉到更换。这就是六边形架构。
\n\n核心概念:
\n- \n
- 端口——定义外部世界如何与核心交互的接口。例如,包含
Save和FindByID方法的UserRepository。 \n - 适配器——端口的具体实现。一个端口可以有多个适配器:PostgreSQLAdapter、InMemoryAdapter、用于测试的MockAdapter。 \n
- 核心——对外部世界一无所知的业务逻辑。它仅通过端口工作。 \n
这种方法带来三大超能力:可测试性(无需数据库即可测试核心)、灵活性(可替换任何组件)、清晰的职责边界。
\n\n为什么Go是六边形架构的理想选择
\n\nGo不强制使用面向对象编程,但提供了接口——这正是端口与适配器所需的唯一特性。Go的接口是隐式的:只要结构体实现了接口的方法,就自动满足该接口。这意味着您可以在不修改核心任何代码的情况下替换适配器。
\n\n此外,Go编译为二进制文件,使该架构对微服务特别有用。内置的测试和基准测试功能让您可以立即验证组件的隔离性。
\n\n项目结构:如何组织文件夹
\n\n虽然没有统一标准,但以下是生产项目中经过验证的方案:
\n\nmyapp/\n├── cmd/\n│ └── app/\n│ └── main.go # 入口点,依赖组装\n├── internal/\n│ ├── core/\n│ │ ├── domain/ # 实体,值对象\n│ │ ├── ports/ # 接口(端口)\n│ │ └── services/ # 业务逻辑(用例)\n│ └── adapters/\n│ ├── api/ # HTTP/gRPC适配器\n│ ├── db/ # 仓库实现\n│ └── mq/ # 消息队列\n└── go.mod\n\n\n重要提示:internal文件夹禁止外部导入——非常适合保护核心。适配器独立存放,便于替换。
编写代码:从接口到实现
\n\n让我们创建一个简单的用户管理服务。从核心开始。
\n\n步骤1. 领域与端口
\n\n// internal/core/domain/user.go\npackage domain\n\nimport "time"\n\ntype User struct {\n ID string\n Email string\n Name string\n CreatedAt time.Time\n}\n\n// internal/core/ports/repositories.go\npackage ports\n\nimport "myapp/internal/core/domain"\n\ntype UserRepository interface {\n Save(user domain.User) error\n FindByID(id string) (domain.User, error)\n FindByEmail(email string) (domain.User, error)\