One way is to create an exported interface using methods and make the implementation type non-return. Create a global variable of the interface type and initialize it using the init() package function. You do not need synchronization, since the init() function of the package will only work once, safely.
The functions of the init() package are executed once, automatically, at runtime, before you can reference anything from the package. See Spec: Package Initialization for more details.
For instance:
package dbprovider type Manager interface { AddArticle(article *article.Article) error // Add other methods } type manager struct { db *gorm.DB } var Mgr Manager func init() { db, err := gorm.Open("sqlite3", "../articles.db") if err != nil { log.Fatal("Failed to init db:", err) } Mgr = &manager{db: db} } func (mgr *manager) AddArticle(article *article.Article) (err error) { mgr.db.Create(article) if errs := mgr.db.GetErrors(); len(errs) > 0 { err = errs[0] } return }
Using it:
import "dbprovider" if err := dbprovider.Mgr.AddArticle(someArticle); err != nil {
You can also do this without the init() function, for example:
var Mgr = newManager() func newManager() Manager { db, err := gorm.Open("sqlite3", "../articles.db") if err != nil { log.Fatal("Failed to init db:", err) } return &manager{db: db} }
With this, you can decide to export newManager() , and your package users can decide to use a common Mgr instance, otherwise they can create another Manager , for example. for testing purposes.
Notes. Mgr is an exported global variable and can be assigned to other packages (for example, dbprovider.Mgr = nil ). If you want to avoid this, you must make it unexcited and provide the getter function for it, for example:
var mgr = newManager() func Mgr() Manager { return mgr }
And using it:
err := dbprovider.Mgr().AddArticle(someArticle)