bootstrapify

package module
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Dec 10, 2025 License: MIT Imports: 6 Imported by: 0

README

go-bootstrapify

A lightweight Go library for bootstrapping services and CLI applications with a modular architecture, dependency management, and lifecycle orchestration.

Features

  • Module System: Define reusable modules with clear lifecycle hooks (Configure, Start, Shutdown)
  • Dependency Management: Automatic topological sorting of modules based on dependencies
  • Circular Dependency Detection: Prevents invalid module configurations at startup
  • Graceful Shutdown: Handles SIGTERM, SIGINT, and SIGHUP with proper cleanup in reverse order
  • Logger Integration: Built-in support for zerolog with per-module logging
  • RunModule Helper: Quick module creation with lifecycle hooks without full Module implementation
  • Type-Safe Configuration: Functional options pattern for clean, composable configuration

Installation

go get gitlab.com/bitval/go-bootstrapify

Quick Start

Basic Service
package main

import (
    "github.com/rs/zerolog"
    "gitlab.com/bitval/go-bootstrapify"
)

func main() {
    logger := zerolog.New(os.Stdout).With().Timestamp().Logger()

    service := bootstrapify.NewService(
        bootstrapify.Config{Logger: &logger},
        bootstrapify.Run("startup",
            bootstrapify.RunAfterConfigure(func(m *bootstrapify.RunModule) error {
                m.Logger().Info().Msg("Service configured")
                return nil
            }),
            bootstrapify.RunAfterStart(func(m *bootstrapify.RunModule) error {
                m.Logger().Info().Msg("Service started")
                return nil
            }),
            bootstrapify.RunAfterShutdown(func(m *bootstrapify.RunModule) error {
                m.Logger().Info().Msg("Service shutting down")
                return nil
            }),
        ),
    )

    if err := service.Start(); err != nil {
        logger.Fatal().Err(err).Msg("Service failed")
    }
}
Module with Dependencies
type DatabaseModule struct {
    modules.Base
    db *sql.DB
}

func (m *DatabaseModule) Configure(opts ...modules.Option) error {
    // Apply options (logger, etc.)
    for _, opt := range opts {
        if err := opt.Apply(m); err != nil {
            return err
        }
    }

    // Initialize database
    var err error
    m.db, err = sql.Open("postgres", "...")
    return err
}

func (m *DatabaseModule) Start() error {
    return m.db.Ping()
}

func (m *DatabaseModule) Shutdown() error {
    return m.db.Close()
}

type APIModule struct {
    modules.Base
    server *http.Server
}

func (m *APIModule) Configure(opts ...modules.Option) error {
    for _, opt := range opts {
        if err := opt.Apply(m); err != nil {
            return err
        }
    }

    // Access database module
    db := m.Dependency("database").(*DatabaseModule)

    m.server = &http.Server{Addr: ":8080"}
    return nil
}

func (m *APIModule) Start() error {
    go m.server.ListenAndServe()
    return nil
}

func (m *APIModule) Shutdown() error {
    return m.server.Close()
}

func main() {
    logger := zerolog.New(os.Stdout).With().Timestamp().Logger()

    dbModule := &DatabaseModule{}
    dbModule.SetName("database")

    apiModule := &APIModule{}
    apiModule.SetName("api")
    apiModule.AddDependencies("database") // API depends on database

    service := bootstrapify.NewService(
        bootstrapify.Config{Logger: &logger},
        bootstrapify.Module(dbModule),
        bootstrapify.Module(apiModule),
    )

    if err := service.Start(); err != nil {
        logger.Fatal().Err(err).Msg("Service failed")
    }
}

Module Lifecycle

Modules follow a three-phase lifecycle:

  1. Configure: Initialize resources, load configuration, set up dependencies
  2. Start: Begin active operations (start servers, open connections, etc.)
  3. Shutdown: Clean up resources in reverse dependency order

The Runtime automatically:

  • Orders modules based on dependencies using topological sort
  • Detects circular dependencies at startup
  • Calls Configure → Start in dependency order
  • Calls Shutdown in reverse dependency order
  • Propagates errors with context

Architecture

Module Interface
type Module interface {
    Configure(...Option) error
    Start() error
    Shutdown() error

    SetName(string)
    Name() string

    AddDependencies(...string)
    SetDependency(Module)
    Dependency(string) Module
    Dependencies() map[string]Module

    SetLogger(*zerolog.Logger)
    Logger() *zerolog.Logger
    Log(zerolog.Level, string, ...any)
}
Base Module

The modules.Base struct provides default implementations for common Module interface methods. Embed it in your custom modules:

type MyModule struct {
    modules.Base
    // your fields
}
RunModule

For simple modules, use RunModule instead of implementing the full Module interface:

bootstrapify.Run("mymodule",
    bootstrapify.RunAfter("database"), // Dependencies
    bootstrapify.RunAfterConfigure(func(m *bootstrapify.RunModule) error {
        // Configuration logic
        return nil
    }),
    bootstrapify.RunAfterStart(func(m *bootstrapify.RunModule) error {
        // Startup logic
        return nil
    }),
    bootstrapify.RunAfterShutdown(func(m *bootstrapify.RunModule) error {
        // Cleanup logic
        return nil
    }),
)

Status

⚠️ Early Alpha - API may change. Not recommended for production use yet.

License

MIT License - Copyright (c) 2025 Tomaz Lovrec

Contributing

This project was extracted from go-auth-api. Contributions welcome!

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewRunModule

func NewRunModule(name string, opts ...RunModuleOption) modules.Module

Types

type Config

type Config struct {
	Logger *zerolog.Logger

	// Transient service is a short lived service and will automatically
	// shutdown all components, including the run component, after their Start
	// procedures have been completed.
	Transient bool
}

type Option

type Option func(*Service) error

func Module

func Module(c modules.Module, opts ...modules.Option) Option

func Run

func Run(name string, opts ...RunModuleOption) Option

type RunModule

type RunModule struct {
	modules.Base
	// contains filtered or unexported fields
}

func (*RunModule) Configure

func (m *RunModule) Configure(opts ...modules.Option) error

func (*RunModule) Shutdown

func (m *RunModule) Shutdown() error

func (*RunModule) Start

func (m *RunModule) Start() error

type RunModuleHook

type RunModuleHook func(*RunModule) error

type RunModuleHooks

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

type RunModuleOption

type RunModuleOption func(*RunModule)

func RunAfter

func RunAfter(dependencies ...string) RunModuleOption

func RunAfterConfigure

func RunAfterConfigure(fn RunModuleHook) RunModuleOption

func RunAfterShutdown

func RunAfterShutdown(fn RunModuleHook) RunModuleOption

func RunAfterStart

func RunAfterStart(fn RunModuleHook) RunModuleOption

func RunModuleOptions

func RunModuleOptions(opts ...RunModuleOption) RunModuleOption

type Service

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

func NewService

func NewService(cfg Config, opts ...Option) *Service

func (Service) Shutdown

func (svc Service) Shutdown() error

func (Service) Start

func (svc Service) Start() error

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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