Using a singleton database class with separate model and service packages

My question is similar to How to create a Singleton DB class in GoLang , but I had problems working with individual packages of models and services.

Project / Library / database /mysql.go:

package database import ( "fmt" _ "github.com/go-sql-driver/mysql" "github.com/jinzhu/gorm" ) type Manager struct { *gorm.DB } var Mgr *Manager func init() { dsn := MysqlConnectionString("parseTime=true") tablePrefix := "demo" var err error gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string { return fmt.Sprintf("%v_%v", tablePrefix, defaultTableName) } db, err := gorm.Open("mysql", dsn) if err != nil { panic(err) } Mgr = &Manager{db} } 

Project / Library / model / retailer_keys.go

 package models import ( "fmt" "project/lib/database" "time" ) type RetailerKeysInterface interface { RetailerKeys() ([]*RetailerKey, error) } type DB struct { database.Manager } type RetailerKey struct { Id int `json:"id"` RetailerId int `json:"retailer_id"` Key string `json:"key"` Enabled *bool `json:"enabled"` CreatedAt *time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` } func (db *DB) RetailerKeys() ([]*RetailerKey, error) { var keys []*RetailerKey if err := db.Find(&keys).Error; err != nil { return nil, err } return keys, nil } 

Project / Library / services / retailer_keys.go

 import ( "fmt" "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/apigateway" "gopkg.in/volatiletech/null.v6" "project/lib/models" "project/lib/services/api_keys" ) func GetKeys() ([]*models.RetailerKey, error) { var q models.RetailerKeysInterface keys, err := q.RetailerKeys() if err != nil { return nil, err } return keys, nil } func CreateKey(id int) (models.RetailerKey, error) { ... } 

Then you can use it in my main package, for example:

 package main import ( "context" "encoding/json" // "reflect" "fmt" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" _ "project/lib/config" "project/lib/services" ) func Handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { statusCode := 200 keys, err := services.GetKeys() if err != nil { statusCode = 400 } body, _ := json.Marshal(keys) return events.APIGatewayProxyResponse{ Body: string(body), StatusCode: statusCode, }, nil } ... 

I would like to be able to embed a corresponding subset of type Manager in my models.

EDIT: Edited the question / code based on the feedback in the comments.

This gives me an error: runtime error: invalid memory address or nil pointer dereference .

+1
source share
1 answer

I definitely misunderstood the interfaces in go. Passing through the Tour Tour, he became more clear how it all fits together.

This is what I did for everyone who is experiencing the same. I will leave the original question so that you can see the differences.

Project / Library / database /mysql.go:

 package database import ( "fmt" "log" _ "github.com/go-sql-driver/mysql" // Needed for gorm "github.com/jinzhu/gorm" ) var Manager *gorm.DB func init() { var err error dsn := MysqlConnectionString("parseTime=true") tablePrefix := "qcommerce" gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string { return fmt.Sprintf("%v_%v", tablePrefix, defaultTableName) } Manager, err = gorm.Open("mysql", dsn) if err != nil { log.Fatal(err) } if err := Manager.DB().Ping(); err != nil { log.Fatal(err) } } 

Project / Library / model / retailer_keys.go

 package models import ( "project/lib/database" "time" ) type QRetailerKeys interface { Insert() error Find() error } type RetailerKey struct { ID int `json:"id"` RetailerID int `json:"retailer_id"` Retailer Retailer `json:"retailer"` Key string `json:"key"` Enabled bool `json:"enabled" gorm:"DEFAULT:true"` CreatedAt *time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` } // RetailerKeys returns a slice of all keys in table func RetailerKeys() ([]*RetailerKey, error) { var keys []*RetailerKey if err := database.Manager.Find(&keys).Error; err != nil { return nil, err } return keys, nil } func (r *RetailerKey) Find() error { ... } // Create a new key func (r *RetailerKey) Create() error { return database.Manager.Create(&r).Error } 

Project / Library / services / retailer_keys.go

 package services import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" // "github.com/aws/aws-sdk-go/service/apigateway" "partners.drinks.com/lib/models" "partners.drinks.com/lib/services/api_keys" ) func sessionBuilder() *session.Session { config := &aws.Config{ Region: aws.String("us-west-2"), } session := session.Must(session.NewSession(config)) return session } func GetKeys() ([]*models.RetailerKey, error) { keys, err := models.RetailerKeys() if err != nil { return nil, err } return keys, nil } func CreateKey(id int) (models.RetailerKey, error) { apikeys := &api_keys.ApiKeyBuilder{} base64Key := apikeys.GenUUID().GenKey().Base64 var key = models.RetailerKey{ RetailerID: id, Key: base64Key, Enabled: func(b bool) } if err := key.Create(); err != nil { return models.RetailerKey{}, err } ... return key, nil } 

I use it like:

 package main import ( "context" "encoding/json" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" _ "partners.drinks.com/lib/config" "partners.drinks.com/lib/services" ) func Handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { statusCode := 200 keys, err := services.GetKeys() if err != nil { statusCode = 400 } body, _ := json.Marshal(keys) return events.APIGatewayProxyResponse{ Body: string(body), StatusCode: statusCode, }, nil } ... 

Thanks to @mkopriva for related resources in the comments.

0
source

Source: https://habr.com/ru/post/1261633/