端口是领域层定义的接口(如userrepository),适配器是实现该接口的具体结构体(如postgresuserrepo),二者共同实现职责隔离:领域逻辑不依赖具体技术实现。

端口适配器在 Go 中不是接口定义,而是具体实现的边界封装
Go 语言没有“端口与适配器”原生语法支持,所谓“端口”只是你定义的 interface{},而“适配器”是满足该接口的结构体 + 方法。关键不在命名,而在职责隔离:领域逻辑不依赖 HTTP、数据库或外部 API 的具体实现。
常见错误是把 http.HandlerFunc 直接塞进领域层,或者让 UserRepository 直接调用 db.QueryRow。这会导致测试困难、难以替换存储、无法模拟第三方服务。
- 端口(Port)应只声明行为,例如:
type UserRepository interface { Save(ctx context.Context, u User) error } - 适配器(Adapter)必须实现该接口,且仅在此处引入具体依赖,例如:
postgresUserRepo持有*sql.DB,mockUserRepo返回固定数据 - 领域层代码(如 usecase)只接收接口参数,不 import 任何框架或驱动包
HTTP 适配器必须剥离路由和中间件逻辑
HTTP 适配器的唯一职责是:解析请求 → 调用 usecase → 序列化响应。它不该包含 JWT 验证、日志记录、CORS 处理——这些属于外围中间件,应独立于适配器存在。
典型错误是把 authMiddleware 写成 userHandler 的私有方法,或者在 handler 里直接调用 log.Printf。这会让适配器变重,也破坏了“一个适配器只对接一种协议”的原则。
- HTTP 适配器结构体应只持有 usecase 接口,例如:
type UserHTTPHandler struct { uc user.UseCase } - handler 方法只做三件事:解包
http.Request→ 调uc.Create(...)→ 写http.ResponseWriter - 路由注册(如
r.POST("/users", h.Create))放在main.go或单独的router.go,不属于适配器
数据库适配器需隐藏 SQL 细节并统一错误映射
数据库适配器不是简单包装 sqlx 或 gorm.DB,它要屏蔽驱动差异,并将底层错误(如 pq.ErrNoRows、mysql.MySQLError)转为领域可理解的错误类型,比如 user.ErrNotFound。
常见坑是直接返回 err,导致上层 usecase 不得不 import 数据库驱动包来判断错误类型;或者把 SQL 字符串拼在 repo 实现里,造成 SQL 散布、难审计。
- SQL 语句建议提取为 const 或 embed 文件,例如:
const sqlInsertUser = "INSERT INTO users (...) VALUES (...)" - 错误处理统一用 switch + errors.Is 或 errors.As,例如:
if errors.Is(err, sql.ErrNoRows) { return nil, user.ErrNotFound } - 事务控制不应由 repo 管理,而应由 usecase 通过
UnitOfWork接口协调多个 repo,避免 repo 自行Begin()/Commit()
测试时适配器要能被零依赖替换,但别过度 mock
单元测试 usecase 时,用内存实现(in-memory adapter)比 mock 更可靠:它走真实逻辑路径,不依赖 mock 框架,也不会因方法签名微调就大面积报错。
很多人一上来就用 gomock 或 testify/mock 生成几十个 mock,结果接口一改、mock 全红,反而掩盖了设计问题。
- 对
UserRepository,写一个memUserRepo,用map[string]User存数据,实现全部方法 - 对 HTTP 适配器,用
httptest.NewServer启一个真实 handler 测试端到端,或用httptest.NewRecorder测单个 handler - 避免 mock 时间相关函数(如
time.Now),应在 usecase 层接受clock.Clock接口,测试时注入固定时间
六边形架构的复杂点从来不在结构图上,而在于每个适配器是否真的只做一件事、是否真的没向内泄漏实现细节。最容易被忽略的是错误类型的统一抽象——如果不同适配器返回五花八门的 error,usecase 就不得不写一堆 if-else 去识别,那端口就名存实亡了。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











