repository 接口应按业务语义设计,方法名体现领域动作(如findactiveordersbycustomerid),返回具体业务结构体,不暴露sql细节,避免手拼sql、跨repo事务、mock测试及横切逻辑,保持签名纯净。

Repository 接口定义要按业务语义,别照着数据库表硬搬
Go 里 Repository 不是 ORM 的包装层,而是业务数据访问契约。接口方法名得反映领域动作,比如 FindActiveOrdersByCustomerID 比 FindByStatusAndCustomerID 更清晰;返回类型用具体业务结构体(Order、Payment),而非 map[string]interface{} 或裸 sql.Rows。
常见错误:把 Create 写成接收 *sql.DB 和一堆字段参数,导致调用方要拼 SQL 条件;正确做法是接收一个完整结构体(如 order.Order),让实现层决定怎么存。
- 接口方法不暴露底层细节:不出现
Scan、Exec、Rows等词 - 避免泛型接口过早引入:先写具体类型(
OrderRepo),等真有多个相似实体再抽象Repository[T] - 错误返回统一用自定义 error(如
ErrNotFound),别直接透传sql.ErrNoRows
SQL 实现层别在 Repository 里写 raw query 字符串拼接
手拼 SQL 容易漏转义、错占位符、难测难维护。Go 标准库 database/sql 支持 ?(SQLite/MySQL)或 $1(PostgreSQL)占位符,必须用它;更推荐用轻量级构建器如 sqlx 或 squirrel,而不是自己字符串 + fmt.Sprintf。
典型翻车现场:"SELECT * FROM orders WHERE customer_id = " + strconv.Itoa(id) —— 直接 SQL 注入,且 PostgreSQL 会报错(整数不能直接拼进字符串)。
- 所有查询参数必须走
db.Query/db.Exec的变参,由驱动处理绑定 - 复杂条件用
squirrel.Select(...).Where(squirrel.Eq{"status": "active"})这类可组合 DSL,别手写 if-else 拼 SQL - 事务内操作不要跨多个 Repository 实例:传同一个
*sql.Tx进各实现的CreateTx方法,而不是每个 repo 自己开 tx
Mock 测试时别 mock 接口,要 fake 实现
Go 的 interface 很轻,但用 gomock 或手工 mock 接口容易写出“假通过真失败”的测试:mock 返回了预期值,却掩盖了实际 SQL 错误、空指针或事务未提交等问题。
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
更可靠的做法是写一个内存 fake 实现(如 memOrderRepo),用 map[int]*Order 存数据,实现全部接口方法。这样单元测试跑的是真实逻辑流,还能覆盖边界(如并发读写 map 需加锁)。
- fake 实现不用支持全部功能:测试订单创建就只实现
Create和FindByID,其余方法 panic 或返回ErrNotImplemented - 避免在 fake 里引入第三方依赖(如 Redis client),否则测试变集成测试
- 如果必须 mock(比如依赖外部 HTTP 服务),确保 mock 行为和真实 error 分类一致(如网络超时 vs 404)
别让 Repository 承担缓存、重试、日志这些横切逻辑
缓存该在 usecase 层或独立 service 做(比如先查 Redis,没命中再调 repo.FindByID);重试应由调用方根据 error 类型决定(如临时网络错误才重试,主键冲突绝不重试);日志打点放在 handler 或 middleware 更合适。
一旦在 repo.FindByID 里塞 log.Printf 或 redis.Get,这个 repo 就没法复用到 CLI 工具或离线任务里——它绑死了运行时环境。
- Repository 接口签名必须纯净:输入结构体/ID,输出领域对象/error,不依赖全局变量或 context.Value
- 需要上下文信息(如租户 ID、请求 trace ID)?通过参数传,别从
context.Context里取 —— 否则测试时得构造带 value 的 context - 性能敏感路径(如高并发查用户)才考虑加缓存,且缓存策略(TTL、穿透保护)和 repo 解耦
真正难的不是写一个能跑的 Repository,是守住它的边界:它只回答“数据在哪、怎么拿”,不关心“谁在用、为什么用、用完干啥”。越早划清这道线,后面加监控、换数据库、拆微服务时踩的坑就越少。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










