Using this structure with MongoDB #69
-
Hey Ben, thanks for the insight. Quite a different structure from what I've learned so far. I wanted to try to apply this structure to a backend, but there is still some issue when trying to connect to MongoDB this way. Here is what I got so far: package data
type DB struct {
Client *mongo.Client
UserCollection *mongo.Collection
TokenCollection *mongo.Collection
ctx context.Context
cancel func()
URI string
MaxIdleTime string
MaxOpenConns uint64
}
func NewDB(uri string) *DB {
db := &DB{
URI: uri,
}
db.ctx, db.cancel = context.WithTimeout(context.Background(), 10*time.Second)
return db
}
func (db *DB) Open() error {
var err error
if db.URI == "" {
return fmt.Errorf("uri required")
}
if db.Client, err = db.newClient(db.URI); err != nil {
return err
}
db.UserCollection = db.openCollection(db.Client, "dbname", "user")
db.TokenCollection = db.openCollection(db.Client, "dbname", "token")
return nil
}
func (db *DB) newClient(uri string) (*mongo.Client, error) {
duration, err := time.ParseDuration(db.MaxIdleTime)
if err != nil {
return nil, err
}
clientOpts := options.Client().ApplyURI(uri).SetMaxPoolSize(db.MaxOpenConns).SetMaxConnIdleTime(duration)
client, err := mongo.Connect(db.ctx, clientOpts)
if err != nil {
return nil, err
}
err = client.Ping(db.ctx, readpref.Primary())
if err != nil {
return nil, err
}
return client, nil
}
func (db *DB) openCollection(client *mongo.Client, dbName, coll string) *mongo.Collection {
collection := client.Database(dbName).Collection(coll)
return collection
}
func (db *DB) Close() error {
db.cancel()
if db.Client != nil {
return db.Client.Disconnect(db.ctx)
}
return nil
} Now for the main package: package main
func main() {
ctx, cancel := context.WithCancel(context.Background())
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() { <-c; cancel() }()
m := NewMain()
m.ParseFlags()
if err := m.Run(ctx); err != nil {
m.Close()
fmt.Fprintln(os.Stderr, err)
appname.ReportError(ctx, err)
os.Exit(1)
}
<-ctx.Done()
if err := m.Close(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
type Main struct {
Config Config
DB *data.DB
HTTPServer *http.Server
UserService appname.UserService
TokenService appname.TokenService
}
func NewMain() *Main {
return &Main{
Config: DefaultConfig(),
DB: data.NewDB(""),
HTTPServer: http.NewServer(),
}
}
func (m *Main) Close() error {
if m.HTTPServer != nil {
if err := m.HTTPServer.Close(); err != nil {
return err
}
}
if m.DB != nil {
if err := m.DB.Close(); err != nil {
return err
}
}
return nil
}
func (m *Main) ParseFlags() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
cfg := DefaultConfig()
flag.IntVar(&cfg.port, "port", 5000, "Server port")
flag.StringVar(&cfg.env, "env", "development", "Environment (development|staging|production)")
flag.StringVar(&cfg.db.uri, "db-uri", os.Getenv("MONGODB_URI"), "MongoDB URI")
flag.IntVar(&cfg.db.maxOpenConns, "db-max-open-conns", 100, "MongoDB max open connections")
flag.StringVar(&cfg.db.maxIdleTime, "db-max-idle-time", "15m", "MongoDB max connection idle time")
flag.Parse()
m.Config = cfg
}
func (m *Main) Run(ctx context.Context) (err error) {
if err := m.DB.Open(); err != nil {
return fmt.Errorf("cannot open db: %w", err)
}
if err := m.HTTPServer.Open(); err != nil {
return err
}
return nil
}
type Config struct {
port int
env string
db struct {
uri string
maxOpenConns int
maxIdleTime string
}
}
func DefaultConfig() Config {
var config Config
config.db.uri = os.Getenv("MONGODB_URI")
return config
} The problem occurs when What am I missing here? Did I forget a * or & somewhere? 🤔 Thanks in advance and please let me know if you need more info. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 4 replies
-
Btw. tried it with RavenDB today, the problem remains the same: The string (here: |
Beta Was this translation helpful? Give feedback.
-
hi - when you're initializing your config, it isn't actually passed down to your dependencies in the current iteration - &Main{
Config: DefaultConfig(),
DB: data.NewDB(""),
HTTPServer: http.NewServer(),
} from To fix your immediate problem, you'd at least want to pass the configured URI into your NewDB func: m := &Main{}
m.ParseFlags()
// now, m.Config is populated
m.DB = data.NewDB(m.Config.db.uri) However, I'd also think about maybe returning the cfg := ParseFlags()
m := &Main{
DB: data.NewDB(cfg.db.uri),
// other components initialized with `cfg` values
} I hope that helps - sorry for the drive-by comment, maybe @benbjohnson has better thoughts here 😄 |
Beta Was this translation helpful? Give feedback.
hi -
when you're initializing your config, it isn't actually passed down to your dependencies in the current iteration -
m
is still:from
NewMain
.To fix your immediate problem, you'd at least want to pass the configured URI into your NewDB func:
However, I'd also think about maybe returning the
Config
object fromParseFlags
and then constructing your components as needed:I hope tha…