我认为这个问题没有一个好的答案,多种方法可能都不错,从特定角度来看一种更好,从另一个角度来看另一种更好。
只需尝试创建一个隐藏 DB 特定行为和类型的接口(不是 Go 接口类型的含义),这将使您稍后可以轻松切换到新的 db 实现,因为所有其他部分您的代码通过此 DB 接口严格访问持久层。
DB 接口应该定义 Go 模型类型(存储在持久层中的建模数据),以及对这些类型的操作,例如加载、查找、保存。
例如,为用户建模:
package db
type ID int64
type User struct {
ID ID
Name string
}
// Manager contains the operations that involve the persistence layer.
type Manager interface {
LoadUser(id ID) (*User, error)
SaveUser(u *User) error
FindUsersByName(name string) ([]*User, error)
Close() error
}
您可以创建Manager 接口的一个(或多个)实现。使用 MongoDB 的实现:
package mongo
import (
"db"
"gopkg.in/mgo.v2"
)
// manager is a db.Manager implementation that uses MongoDB
type manager struct {
// unexported fields, e.g. MongoDB session:
sess *mgo.Sess
}
func (m *manager) LoadUser(id db.ID) (*db.User, error) { ... }
func (m *manager) SaveUser(u *db.User) error { ... }
func (m *manager) FindUsersByName(name string) ([]*db.User, error) { ... }
func (m *manager) Close() error {
m.sess.Close()
return nil
}
func New(mongoURL string) (db.Manager, error) {
// Create, initialize your manager, and return it:
sess, err := mgo.Dial(url)
if err != nil {
return nil, err
}
return &manager{sess: sess}, nil
}
db.Manager 实例(因为它涉及建立与 (Mongo)DB 服务器的连接)应尽可能长时间地保留(例如全局实例)。根据使用情况,应支持 Manager.Copy() 和 Manager.Clone() 操作以获取副本或克隆以供短期使用(例如服务 HTTP 请求)。
使用这个例子:某个地方的某个人必须调用mongo.New() 来获取db.Manager 的值,但是从那里我们只需要通过Manager 与持久层进行交互,将任何数据库特定的细节留给实施。
例如:
var mgr db.Manager
var err error
mgr, err = mongo.New("<mongodburl>")
if err != nil {
log.Printf("Could not connect to db:", err)
return
}
defer mgr.Close()
id := 123456
u, err := mgr.LoadUser(id)
if err != nil {
log.Printf("Failed to load user [id: %v]: %v\n", id, err)
return
}
fmt.Printf("Loaded User: %+v\n", u)