Go中如何解决接口与多数据库实现类的返回类型不兼容问题?
解决Go中sqlc多数据库实现与DDD边界接口兼容问题
方案1:领域模型+适配器转换(最贴合DDD)
- 先在领域层(比如
domain包)定义统一的User结构体,把业务需要的字段都放进去:package domain type User struct { ID int64 FirstName string // 其他业务需要的字段 } - 给每个sqlc生成的数据库实现写个适配器层,实现你定义的
storage.UserStorer接口,核心就是把sqlc生成的结构体转成领域层的domain.User。比如sqlite的适配器:package sqliteadapter import ( "context" "your-project/domain" "your-project/storage" "your-project/sqlc/sqlite" ) type Adapter struct { q *sqlite.Queries } func NewAdapter(q *sqlite.Queries) storage.UserStorer { return &Adapter{q: q} } func (a *Adapter) FindUserById(ctx context.Context, id int64) (domain.User, error) { sqlcUser, err := a.q.FindUserById(ctx, id) if err != nil { return domain.User{}, err } // 字段一一映射 return domain.User{ ID: sqlcUser.ID, FirstName: sqlcUser.FirstName, // 其他字段照猫画虎 }, nil } - MySQL和PostgreSQL的适配器照这个逻辑写就行。这样所有适配器都返回统一的
domain.User,完全符合接口要求。消费者只依赖domain.User和storage.UserStorer,切换数据库时只需要初始化对应适配器,不用改任何业务代码。
方案2:用接口统一返回类型(轻量方案)
如果sqlc给各个数据库生成的User结构体字段完全一致,只是包名不同,可以这么搞:
- 重新定义
storage包里的接口,把返回的storage.User改成一个带Getter方法的接口:package storage import "context" type User interface { GetID() int64 GetFirstName() string // 每个字段对应一个Getter方法 } type UserStorer interface { FindUserById(ctx context.Context, id int64) (User, error) // 其他存储方法 } - 给每个sqlc生成的
User结构体加扩展Getter方法——不用改sqlc生成的代码,直接在对应包下新建文件写就行。比如sqlite的扩展:package sqlite func (u User) GetID() int64 { return u.ID } func (u User) GetFirstName() string { return u.FirstName } - MySQL和PostgreSQL的
User结构体也这么加Getter,这样它们就隐式实现了storage.User接口,*sqlite.Queries的方法返回值就能匹配接口要求,编译报错直接消失。
选哪个?
- 方案1更规范,完全符合DDD的分层思想,把持久化模型和领域模型解耦,就算以后数据库结构变了,只改适配器就行,业务层不受影响。
- 方案2更简单,适合字段稳定、各数据库结构完全一致的场景,代码量更少。
内容的提问来源于stack exchange,提问作者keystroke33
相关产品推荐
相关产品推荐

