database

package module
v0.3.0-alpha.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 26, 2026 License: MIT Imports: 14 Imported by: 0

README

database

GORM-backed database contracts, component lifecycle, transaction helpers, repository helpers, migrations, query builder, and tenant utilities.

Core does not select a backend driver by default. Applications register or inject the driver they need; adapter packages must not use import-time registration.

Explicit driver

import "gorm.io/driver/postgres"

cfg := database.Config{
    Enabled:     true,
    DSN:         "host=localhost user=app dbname=mydb sslmode=disable",
    AutoMigrate: true,
}

comp := database.NewComponent(cfg, log).
    WithDriver(postgres.Open).
    WithAutoMigrate(&User{})

Registry-driven selection

import "gorm.io/driver/postgres"

drivers := database.NewDriverRegistry()
if err := drivers.Register("postgres", postgres.Open); err != nil {
    return err
}

comp := database.NewComponent(cfg, log).
    WithDriverFromRegistry(drivers, "postgres")

SQLite adapter

database/sqlite is a nested adapter module for tests/local development:

import "github.com/kbukum/gokit/database/sqlite"

drivers := database.NewDriverRegistry()
if err := sqlite.Register(drivers); err != nil {
    return err
}

comp := database.NewComponent(database.Config{
    Enabled: true,
    DSN:     ":memory:",
}, log).WithDriverFromRegistry(drivers, sqlite.Name)

Design constraints

  • Component startup requires an explicit driver or registry selection.
  • DriverRegistry stores backend factories without package-level global state.
  • Runtime code stays driver-agnostic; backend adapters register with an application-owned registry.
  • GORM provides the repository/query substrate for the Go implementation.

Documentation

Overview

Package database provides a GORM-based database component with connection pooling, health checks, transactions, and migration support.

Architecture

The database module follows gokit's component pattern with a driver-agnostic design. Users provide the database driver (postgres, mysql, sqlite, etc.) via WithDriver() or an explicit DriverRegistry, keeping runtime backend selection adapter-driven.

Quick Start

Bootstrap the database component in your application:

import (
    "github.com/kbukum/gokit/bootstrap"
    "github.com/kbukum/gokit/database"
    "gorm.io/driver/postgres"
)

func main() {
    app := bootstrap.New()
    cfg := database.Config{Enabled: true, DSN: "host=localhost user=myuser password=mypass dbname=mydb"}
    app.Register(database.NewComponent(cfg, log).
        WithDriver(func(dsn string) gorm.Dialector {
            return postgres.Open(dsn)
        }))
    app.Start(context.Background())
}

Subpackages

  • errors: Database error utilities and translation to AppError
  • types: Common database types like BaseModel
  • migration: File-based database migrations using golang-migrate
  • query: Advanced query builders and helpers
  • sqlite: Opt-in SQLite driver adapter
  • testutil: Testing utilities for database-dependent tests

Optional Component

The database component respects the Enabled flag in configuration. When disabled, Start() returns immediately without initializing the connection, and Health() reports "disabled" status.

cfg := database.Config{Enabled: false}  // Component will be disabled

See component.go for full lifecycle documentation.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ScopeToTenant

func ScopeToTenant(db *gorm.DB, column string, value any) *gorm.DB

ScopeToTenant returns a new GORM session with a WHERE clause filtering by the given tenant column and value. Use this to create a workspace-scoped database session for multi-tenant queries.

Example: scopedDB := ScopeToTenant(db, "workspace_id", workspaceID)

func SetSessionVariable

func SetSessionVariable(db *gorm.DB, name, value string, isLocal bool) error

SetSessionVariable sets a PostgreSQL session variable using set_config(). When isLocal is true, the variable is scoped to the current transaction only. This is used for PostgreSQL Row Level Security (RLS) policies that read session variables via current_setting().

Example: SetSessionVariable(db, "app.workspace_id", workspaceID, true)

Types

type Component

type Component struct {
	// contains filtered or unexported fields
}

Component wraps DB and implements component.Component for lifecycle management.

func NewComponent

func NewComponent(cfg Config, log *logging.Logger) *Component

NewComponent creates a database component for use with the component registry. Drivers are opt-in: call WithDriver or WithDriverFromRegistry before Start. The Config.Enabled flag can be used to skip initialization at runtime.

func (*Component) DB

func (c *Component) DB() *DB

DB returns the underlying *DB, or nil if not started.

func (*Component) Describe

func (c *Component) Describe() component.Description

Describe returns infrastructure summary info for the bootstrap display.

func (*Component) Health

func (c *Component) Health(ctx context.Context) component.Health

Health returns the current health status of the database. If Config.Enabled is false, returns StatusHealthy with "disabled" message. The context is used for the ping operation and honors cancellation.

func (*Component) Name

func (c *Component) Name() string

Name returns the component name.

func (*Component) Start

func (c *Component) Start(ctx context.Context) error

Start connects to the database and optionally runs auto-migration. If Config.Enabled is false, this method returns immediately without error. The context is used for connection retries and can be canceled to abort startup.

func (*Component) Stop

func (c *Component) Stop(_ context.Context) error

Stop gracefully closes the database connection.

func (*Component) WithAutoMigrate

func (c *Component) WithAutoMigrate(models ...any) *Component

WithAutoMigrate registers models for auto-migration on Start. Models are only migrated if Config.AutoMigrate is true and the component is enabled.

func (*Component) WithDriver

func (c *Component) WithDriver(fn DriverFunc) *Component

WithDriver sets the database driver function. Pass the Open function from your chosen driver (not the result of calling it).

Example:

import "gorm.io/driver/postgres" db := database.NewComponent(cfg, log).

WithDriver(postgres.Open).
WithAutoMigrate(&User{}, &Post{})

func (*Component) WithDriverFromRegistry

func (c *Component) WithDriverFromRegistry(reg *DriverRegistry, name string) *Component

WithDriverFromRegistry selects a registered driver by name.

type Config

type Config struct {
	// Name identifies this adapter instance (used by provider.Provider interface).
	Name string `yaml:"name" mapstructure:"name"`

	// Enabled controls whether the database component is active.
	Enabled bool `yaml:"enabled" mapstructure:"enabled"`

	// DSN is the full database connection string (legacy). When set,
	// it takes precedence over structured fields. Prefer using Host/Port/DBName/User/Password instead.
	DSN string `yaml:"dsn" mapstructure:"dsn"`

	// Host is the database server hostname or IP.
	Host string `yaml:"host" mapstructure:"host"`

	// Port is the database server port.
	Port int `yaml:"port" mapstructure:"port"`

	// DBName is the database name.
	DBName string `yaml:"db_name" mapstructure:"db_name"`

	// User is the database user.
	User string `yaml:"user" mapstructure:"user"`

	// Password is the database password (from env var, not committed).
	Password string `yaml:"password" mapstructure:"password"`

	// SSLMode controls the SSL connection mode (e.g. "disable", "require").
	SSLMode string `yaml:"ssl_mode" mapstructure:"ssl_mode"`

	// Resolve is the discovery service name for this database. Empty = use static Host:Port.
	// Set = resolve from discovery provider.
	Resolve string `yaml:"resolve" mapstructure:"resolve"`

	// MaxOpenConns sets the maximum number of open connections to the database.
	MaxOpenConns int `yaml:"max_open_conns" mapstructure:"max_open_conns"`

	// MaxIdleConns sets the maximum number of idle connections in the pool.
	MaxIdleConns int `yaml:"max_idle_conns" mapstructure:"max_idle_conns"`

	// ConnMaxLifetime is the maximum time a connection may be reused (e.g. "1h", "30m").
	ConnMaxLifetime string `yaml:"conn_max_lifetime" mapstructure:"conn_max_lifetime"`

	// ConnMaxIdleTime is the maximum time a connection may sit idle (e.g. "5m", "10m"). If empty,
	// no idle timeout is set.
	ConnMaxIdleTime string `yaml:"conn_max_idle_time" mapstructure:"conn_max_idle_time"`

	// MaxRetries is the number of connection attempts before giving up.
	MaxRetries int `yaml:"max_retries" mapstructure:"max_retries"`

	// AutoMigrate controls whether GORM auto-migration runs on startup.
	AutoMigrate bool `yaml:"auto_migrate" mapstructure:"auto_migrate"`

	// SlowQueryThreshold is the duration above which queries are logged as slow (e.g. "200ms").
	SlowQueryThreshold string `yaml:"slow_query_threshold" mapstructure:"slow_query_threshold"`

	// LogLevel controls GORM's log verbosity: "silent", "error", "warn", "info" (default).
	LogLevel string `yaml:"log_level" mapstructure:"log_level"`
}

Config holds database connection configuration.

func (*Config) ApplyDefaults

func (c *Config) ApplyDefaults()

ApplyDefaults sets sensible defaults for zero-valued fields.

func (*Config) BuildDSN

func (c *Config) BuildDSN() string

BuildDSN constructs a PostgreSQL DSN from structured fields. A pre-set Config.DSN (e.g., a connection string supplied verbatim from a secret store) takes precedence over the individual host/port/user/etc. fields and is returned unchanged.

func (*Config) Validate

func (c *Config) Validate() error

Validate checks that required fields are present and parseable.

type DB

type DB struct {
	GormDB *gorm.DB
	// contains filtered or unexported fields
}

DB wraps a GORM database with gokit logging.

func New

func New(cfg Config, log *logging.Logger, dialector gorm.Dialector) (*DB, error)

New opens a database connection with retry logic and connection pooling. For most use cases, use Component instead which provides driver flexibility via WithDriver().

func NewWithContext

func NewWithContext(ctx context.Context, dialector any, cfg Config, log *logging.Logger) (*DB, error)

NewWithContext creates a database connection with context-aware retry logic. The context allows cancellation of connection attempts during retries.

func (*DB) AutoMigrate

func (d *DB) AutoMigrate(models ...any) error

AutoMigrate runs GORM auto-migration for the given models.

func (*DB) Close

func (d *DB) Close() error

Close closes the underlying sql.DB connection pool. Safe to call multiple times.

func (*DB) IsAvailable

func (db *DB) IsAvailable(ctx context.Context) bool

IsAvailable checks if the database connection is healthy (implements provider.Provider).

func (*DB) Name

func (db *DB) Name() string

Name returns the adapter name (implements provider.Provider).

func (*DB) Ping

func (d *DB) Ping() error

Ping verifies the database connection is alive.

func (*DB) PingContext

func (d *DB) PingContext(ctx context.Context) error

PingContext verifies the database connection is alive, respecting the context.

func (*DB) Transaction

func (d *DB) Transaction(fn func(*gorm.DB) error) error

Transaction executes fn inside a database transaction.

func (*DB) WithContext

func (d *DB) WithContext(ctx context.Context) *gorm.DB

WithContext returns a GORM session scoped to the given context.

func (*DB) WithReadOnlyTransaction

func (d *DB) WithReadOnlyTransaction(ctx context.Context, fn TransactionFunc) error

WithReadOnlyTransaction executes fn in a read-only transaction (always rolls back).

func (*DB) WithTransaction

func (d *DB) WithTransaction(ctx context.Context, fn TransactionFunc) error

WithTransaction executes fn within a transaction with panic recovery.

type DriverFunc

type DriverFunc func(dsn string) gorm.Dialector

DriverFunc creates a GORM dialector from a DSN.

type DriverRegistry

type DriverRegistry struct {
	// contains filtered or unexported fields
}

DriverRegistry stores database driver factories by backend name.

func NewDriverRegistry

func NewDriverRegistry() *DriverRegistry

NewDriverRegistry creates an isolated driver registry.

func (*DriverRegistry) Get

func (r *DriverRegistry) Get(name string) (DriverFunc, bool)

Get returns a driver factory by name.

func (*DriverRegistry) Register

func (r *DriverRegistry) Register(name string, fn DriverFunc) error

Register stores a driver factory.

type TransactionFunc

type TransactionFunc func(tx *gorm.DB) error

TransactionFunc defines a function that runs within a transaction.

Directories

Path Synopsis
Package errors classifies driver-level database errors into portable categories.
Package errors classifies driver-level database errors into portable categories.
Package migration runs schema migrations against a database.
Package migration runs schema migrations against a database.
Package query translates request-level filtering, faceting, and includes into GORM query clauses.
Package query translates request-level filtering, faceting, and includes into GORM query clauses.
Package repository provides a generic CRUD repository pattern over the database abstraction, with transaction support, optimistic locking, and built-in pagination/filtering helpers.
Package repository provides a generic CRUD repository pattern over the database abstraction, with transaction support, optimistic locking, and built-in pagination/filtering helpers.
testutil module
Package types holds shared persistence types embedded by domain models.
Package types holds shared persistence types embedded by domain models.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL