modular

package module
v1.2.6 Latest Latest
Warning

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

Go to latest
Published: May 26, 2025 License: MIT Imports: 19 Imported by: 17

README ¶

modular

Modular Go

GitHub License Go Reference CodeQL Dependabot Updates CI Modules CI Go Report Card codecov

Overview

Modular is a package that provides a structured way to create modular applications in Go. It allows you to build applications as collections of modules that can be easily added, removed, or replaced. Key features include:

  • Module lifecycle management: Initialize, start, and gracefully stop modules
  • Dependency management: Automatically resolve and order module dependencies
  • Service registry: Register and retrieve application services
  • Configuration management: Handle configuration for modules and services
  • Configuration validation: Validate configurations with defaults, required fields, and custom logic
  • Sample config generation: Generate sample configuration files in various formats
  • Dependency injection: Inject required services into modules
  • Multi-tenancy support: Build applications that serve multiple tenants with isolated configurations

🧩 Available Modules

Modular comes with a rich ecosystem of pre-built modules that you can easily integrate into your applications:

Core Modules
  • Auth - Comprehensive authentication with JWT, sessions, password hashing, and OAuth2/OIDC support
  • Cache - Multi-backend caching with Redis and in-memory support
  • Database - Database connectivity and management with multiple driver support
  • Event Bus - Asynchronous event handling and pub/sub messaging
Network & Communication
  • Chi Router (Chimux) - HTTP routing with Chi router integration and middleware support
  • Reverse Proxy - Advanced reverse proxy with load balancing, circuit breaker, and health checks
Utilities & Processing
  • JSON Schema - JSON schema validation and processing
  • Scheduler - Job scheduling with cron expressions and worker pools

Each module is designed to be:

  • Plug-and-play: Easy integration with minimal configuration
  • Configurable: Extensive configuration options via YAML, environment variables, or code
  • Production-ready: Built with best practices, proper error handling, and comprehensive testing
  • Well-documented: Complete documentation with examples and API references

📖 For detailed information about each module, see the modules directory or click on the individual module links above.

Installation

go get github.com/GoCodeAlone/modular

Usage

Basic Application
package main

import (
    "github.com/GoCodeAlone/modular"
    "log/slog"
    "os"
)

func main() {
    // Create a logger
    logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
    
    // Create config provider with application configuration
    config := &AppConfig{
        Name: "MyApp",
        Version: "1.0.0",
    }
    configProvider := modular.NewStdConfigProvider(config)
    
    // Create the application
    app := modular.NewStdApplication(configProvider, logger)
    
    // Register modules
    app.RegisterModule(NewDatabaseModule())
    app.RegisterModule(NewAPIModule())
    
    // Run the application (this will block until the application is terminated)
    if err := app.Run(); err != nil {
        logger.Error("Application error", "error", err)
        os.Exit(1)
    }
}
Creating a Module
type DatabaseModule struct {
    db     *sql.DB
    config *DatabaseConfig
}

func NewDatabaseModule() modular.Module {
    return &DatabaseModule{}
}

// RegisterConfig registers the module's configuration
func (m *DatabaseModule) RegisterConfig(app modular.Application) error {
    m.config = &DatabaseConfig{
        DSN: "postgres://user:password@localhost:5432/dbname",
    }
    app.RegisterConfigSection("database", modular.NewStdConfigProvider(m.config))
    return nil
}

// Name returns the module's unique name
func (m *DatabaseModule) Name() string {
    return "database"
}

// Dependencies returns other modules this module depends on
func (m *DatabaseModule) Dependencies() []string {
    return []string{} // No dependencies
}

// Init initializes the module
func (m *DatabaseModule) Init(app modular.Application) error {
    // Initialize database connection
    db, err := sql.Open("postgres", m.config.DSN)
    if (err != nil) {
        return err
    }
    m.db = db
    return nil
}

// ProvidesServices returns services provided by this module
func (m *DatabaseModule) ProvidesServices() []modular.ServiceProvider {
    return []modular.ServiceProvider{
        {
            Name:        "database",
            Description: "Database connection",
            Instance:    m.db,
        },
    }
}

// RequiresServices returns services required by this module
func (m *DatabaseModule) RequiresServices() []modular.ServiceDependency {
    return []modular.ServiceDependency{} // No required services
}

// Start starts the module
func (m *DatabaseModule) Start(ctx context.Context) error {
    return nil // Database is already connected
}

// Stop stops the module
func (m *DatabaseModule) Stop(ctx context.Context) error {
    return m.db.Close()
}
Service Dependencies
// A module that depends on another service
func (m *APIModule) RequiresServices() []modular.ServiceDependency {
    return []modular.ServiceDependency{
        {
            Name:     "database",
            Required: true,  // Application won't start if this service is missing
        },
        {
            Name:     "cache",
            Required: false, // Optional dependency
        },
    }
}

// Using constructor injection
func (m *APIModule) Constructor() modular.ModuleConstructor {
    return func(app modular.Application, services map[string]any) (modular.Module, error) {
        // Services that were requested in RequiresServices() are available here
        db := services["database"].(*sql.DB)
        
        // Create a new module instance with injected services
        return &APIModule{
            db: db,
        }, nil
    }
}
Interface-Based Service Matching

Modular supports finding and injecting services based on interface compatibility, regardless of the service name:

// Define an interface that services should implement
type LoggerService interface {
    Log(level string, message string)
}

// Require a service that implements a specific interface
func (m *MyModule) RequiresServices() []modular.ServiceDependency {
    return []modular.ServiceDependency{
        {
            Name:               "logger", // The name you'll use to access it in the Constructor
            Required:           true,
            MatchByInterface:   true, // Enable interface-based matching
            SatisfiesInterface: reflect.TypeOf((*LoggerService)(nil)).Elem(),
        },
    }
}

// Constructor will receive any service implementing LoggerService
func (m *MyModule) Constructor() modular.ModuleConstructor {
    return func(app modular.Application, services map[string]any) (modular.Module, error) {
        // This will work even if the actual registered service name is different
        logger := services["logger"].(LoggerService)
        return &MyModule{logger: logger}, nil
    }
}

See DOCUMENTATION.md for more advanced details about service dependencies and interface matching.

Configuration Management
// Define your configuration struct
type AppConfig struct {
    Name    string `json:"name" yaml:"name" default:"DefaultApp" desc:"Application name"`
    Version string `json:"version" yaml:"version" required:"true" desc:"Application version"`
    Debug   bool   `json:"debug" yaml:"debug" default:"false" desc:"Enable debug mode"`
}

// Implement ConfigValidator interface for custom validation
func (c *AppConfig) Validate() error {
    // Custom validation logic
    if c.Version == "0.0.0" {
        return fmt.Errorf("invalid version: %s", c.Version)
    }
    return nil
}
Configuration Validation and Default Values

Modular now includes powerful configuration validation features:

Default Values with Struct Tags
// Define struct with default values
type ServerConfig struct {
    Host        string `yaml:"host" default:"localhost" desc:"Server host"`
    Port        int    `yaml:"port" default:"8080" desc:"Server port"`
    ReadTimeout int    `yaml:"readTimeout" default:"30" desc:"Read timeout in seconds"`
    Debug       bool   `yaml:"debug" default:"false" desc:"Enable debug mode"`
}

Default values are automatically applied to fields that have zero/empty values when configurations are loaded.

Required Field Validation
type DatabaseConfig struct {
    Host     string `yaml:"host" default:"localhost" desc:"Database host"`
    Port     int    `yaml:"port" default:"5432" desc:"Database port"`
    Name     string `yaml:"name" required:"true" desc:"Database name"` // Must be provided
    User     string `yaml:"user" default:"postgres" desc:"Database user"`
    Password string `yaml:"password" required:"true" desc:"Database password"` // Must be provided
}

Required fields are validated during configuration loading, and appropriate errors are returned if they're missing.

Custom Validation Logic
// Implement the ConfigValidator interface
func (c *AppConfig) Validate() error {
    // Validate environment is one of the expected values
    validEnvs := map[string]bool{"dev": true, "test": true, "prod": true}
    if !validEnvs[c.Environment] {
        return fmt.Errorf("%w: environment must be one of [dev, test, prod]", modular.ErrConfigValidationFailed)
    }
    
    // Additional custom validation
    if c.Server.Port < 1024 || c.Server.Port > 65535 {
        return fmt.Errorf("%w: server port must be between 1024 and 65535", modular.ErrConfigValidationFailed)
    }
    
    return nil
}
Generating Sample Configuration Files
// Generate a sample configuration file
cfg := &AppConfig{}
err := modular.SaveSampleConfig(cfg, "yaml", "config-sample.yaml")
if err != nil {
    log.Fatalf("Error generating sample config: %v", err)
}

Sample configurations can be generated in YAML, JSON, or TOML formats, with all default values pre-populated.

Command-Line Integration
func main() {
    // Generate sample config file if requested
    if len(os.Args) > 1 && os.Args[1] == "--generate-config" {
        format := "yaml"
        if len(os.Args) > 2 {
            format = os.Args[2]
        }
        outputFile := "config-sample." + format
        if len(os.Args) > 3 {
            outputFile = os.Args[3]
        }
        
        cfg := &AppConfig{}
        if err := modular.SaveSampleConfig(cfg, format, outputFile); err != nil {
            fmt.Printf("Error generating sample config: %v\n", err)
            os.Exit(1)  // Error condition should exit with non-zero code
        }
        fmt.Printf("Sample config generated at %s\n", outputFile)
        os.Exit(0)
    }
    
    // Continue with normal application startup...
}

Multi-Tenant Support

Modular provides built-in support for multi-tenant applications through:

Tenant Contexts
// Creating a tenant context
tenantID := modular.TenantID("tenant1")
ctx := modular.NewTenantContext(context.Background(), tenantID)

// Using tenant context with the application
tenantCtx, err := app.WithTenant(tenantID)
if err != nil {
    log.Fatal("Failed to create tenant context:", err)
}

// Extract tenant ID from a context
if id, ok := modular.GetTenantIDFromContext(ctx); ok {
    fmt.Println("Current tenant:", id)
}
Tenant-Aware Configuration
// Register a tenant service in your module
func (m *MultiTenantModule) ProvidesServices() []modular.ServiceProvider {
    return []modular.ServiceProvider{
        {
            Name:        "tenantService",
            Description: "Tenant management service",
            Instance:    modular.NewStandardTenantService(m.logger),
        },
        {
            Name:        "tenantConfigLoader",
            Description: "Tenant configuration loader",
            Instance:    modular.DefaultTenantConfigLoader("./configs/tenants"),
        },
    }
}

// Create tenant-aware configuration
func (m *MultiTenantModule) RegisterConfig(app *modular.Application) {
    // Default config
    defaultConfig := &MyConfig{
        Setting: "default",
    }
    
    // Get tenant service (must be provided by another module)
    var tenantService modular.TenantService
    app.GetService("tenantService", &tenantService)
    
    // Create tenant-aware config provider
    tenantAwareConfig := modular.NewTenantAwareConfig(
        modular.NewStdConfigProvider(defaultConfig),
        tenantService,
        "mymodule",
    )
    
    app.RegisterConfigSection("mymodule", tenantAwareConfig)
}

// Using tenant-aware configs in your code
func (m *MultiTenantModule) ProcessRequestWithTenant(ctx context.Context) {
    // Get config specific to the tenant in the context
    config, ok := m.config.(*modular.TenantAwareConfig)
    if !ok {
        // Handle non-tenant-aware config
        return
    }
    
    // Get tenant-specific configuration
    myConfig := config.GetConfigWithContext(ctx).(*MyConfig)
    
    // Use tenant-specific settings
    fmt.Println("Tenant setting:", myConfig.Setting)
}
Tenant-Aware Modules
// Implement the TenantAwareModule interface
type MultiTenantModule struct {
    modular.Module
    tenantData map[modular.TenantID]*TenantData
}

func (m *MultiTenantModule) OnTenantRegistered(tenantID modular.TenantID) {
    // Initialize resources for this tenant
    m.tenantData[tenantID] = &TenantData{
        initialized: true,
    }
}

func (m *MultiTenantModule) OnTenantRemoved(tenantID modular.TenantID) {
    // Clean up tenant resources
    delete(m.tenantData, tenantID)
}
Loading Tenant Configurations
// Set up a file-based tenant config loader
configLoader := modular.NewFileBasedTenantConfigLoader(modular.TenantConfigParams{
    ConfigNameRegex: regexp.MustCompile("^tenant-[\\w-]+\\.(json|yaml)$"),
    ConfigDir:       "./configs/tenants",
    ConfigFeeders:   []modular.Feeder{},
})

// Register the loader as a service
app.RegisterService("tenantConfigLoader", configLoader)

Key Interfaces

Module

The core interface that all modules must implement:

type Module interface {
    RegisterConfig(app *Application)
    Init(app *Application) error
    Start(ctx context.Context) error
    Stop(ctx context.Context) error
    Name() string
    Dependencies() []string
    ProvidesServices() []ServiceProvider
    RequiresServices() []ServiceDependency
}
TenantAwareModule

Interface for modules that need to respond to tenant lifecycle events:

type TenantAwareModule interface {
    Module
    OnTenantRegistered(tenantID TenantID)
    OnTenantRemoved(tenantID TenantID)
}
TenantService

Interface for managing tenants:

type TenantService interface {
    GetTenantConfig(tenantID TenantID, section string) (ConfigProvider, error)
    GetTenants() []TenantID
    RegisterTenant(tenantID TenantID, configs map[string]ConfigProvider) error
}
ConfigProvider

Interface for configuration providers:

type ConfigProvider interface {
    GetConfig() any
}
ConfigValidator

Interface for implementing custom configuration validation logic:

type ConfigValidator interface {
    Validate() error
}

CLI Tool

Modular comes with a command-line tool (modcli) to help you create new modules and configurations.

Installation

You can install the CLI tool using one of the following methods:

go install github.com/GoCodeAlone/modular/cmd/modcli@latest

This will download, build, and install the latest version of the CLI tool directly to your GOPATH's bin directory, which should be in your PATH.

Download pre-built binaries

Download the latest release from the GitHub Releases page and add it to your PATH.

Build from source
# Clone the repository
git clone https://github.com/GoCodeAlone/modular.git
cd modular/cmd/modcli

# Build the CLI tool
go build -o modcli

# Move to a directory in your PATH
mv modcli /usr/local/bin/  # Linux/macOS
# or add the current directory to your PATH
Usage

Generate a new module:

modcli generate module --name MyFeature

Generate a configuration:

modcli generate config --name Server

For more details on available commands:

modcli --help

Each command includes interactive prompts to guide you through the process of creating modules or configurations with the features you need.

License

MIT License

Documentation ¶

Overview ¶

Package modular provides a flexible, modular application framework for Go. It supports configuration management, dependency injection, service registration, and multi-tenant functionality.

Package modular provides tenant functionality for multi-tenant applications. This file contains tenant-related types and interfaces.

Package modular provides tenant-aware functionality for multi-tenant applications. This file contains the core tenant service implementation.

Index ¶

Constants ¶

This section is empty.

Variables ¶

View Source
var (
	// Configuration errors
	ErrConfigSectionNotFound = errors.New("config section not found")
	ErrApplicationNil        = errors.New("application is nil")
	ErrConfigProviderNil     = errors.New("failed to load app config: config provider is nil")
	ErrConfigSectionError    = errors.New("failed to load app config: error triggered by section")

	// Config validation errors
	ErrConfigNil                  = errors.New("config is nil")
	ErrConfigNotPointer           = errors.New("config must be a pointer")
	ErrConfigNotStruct            = errors.New("config must be a struct")
	ErrConfigRequiredFieldMissing = errors.New("required field is missing")
	ErrConfigValidationFailed     = errors.New("config validation failed")
	ErrUnsupportedTypeForDefault  = errors.New("unsupported type for default value")
	ErrDefaultValueParseError     = errors.New("failed to parse default value")
	ErrDefaultValueOverflowsInt   = errors.New("default value overflows int")
	ErrDefaultValueOverflowsUint  = errors.New("default value overflows uint")
	ErrDefaultValueOverflowsFloat = errors.New("default value overflows float")
	ErrInvalidFieldKind           = errors.New("invalid field kind")
	ErrIncompatibleFieldKind      = errors.New("incompatible field kind")
	ErrUnexpectedFieldKind        = errors.New("unexpected field kind")
	ErrUnsupportedFormatType      = errors.New("unsupported format type")
	ErrConfigFeederError          = errors.New("config feeder error")
	ErrConfigSetupError           = errors.New("config setup error")
	ErrConfigNilPointer           = errors.New("config is nil pointer")

	// Service registry errors
	ErrServiceAlreadyRegistered = errors.New("service already registered")
	ErrServiceNotFound          = errors.New("service not found")

	// Service injection errors
	ErrTargetNotPointer      = errors.New("target must be a non-nil pointer")
	ErrTargetValueInvalid    = errors.New("target value is invalid")
	ErrServiceIncompatible   = errors.New("service cannot be assigned to target")
	ErrServiceNil            = errors.New("service is nil")
	ErrServiceWrongType      = errors.New("service doesn't satisfy required type")
	ErrServiceWrongInterface = errors.New("service doesn't satisfy required interface")

	// Dependency resolution errors
	ErrCircularDependency      = errors.New("circular dependency detected")
	ErrModuleDependencyMissing = errors.New("module depends on non-existent module")
	ErrRequiredServiceNotFound = errors.New("required service not found for module")

	// Tenant errors
	ErrAppContextNotInitialized        = errors.New("application context not initialized")
	ErrTenantNotFound                  = errors.New("tenant not found")
	ErrTenantConfigNotFound            = errors.New("tenant config section not found")
	ErrTenantConfigProviderNil         = errors.New("tenant config provider is nil")
	ErrTenantConfigValueNil            = errors.New("tenant config value is nil")
	ErrTenantRegisterNilConfig         = errors.New("cannot register nil config for tenant")
	ErrMockTenantConfigsNotInitialized = errors.New("mock tenant configs not initialized")
	ErrConfigSectionNotFoundForTenant  = errors.New("config section not found for tenant")

	// Test-specific errors
	ErrSetupFailed   = errors.New("setup error")
	ErrFeedFailed    = errors.New("feed error")
	ErrFeedKeyFailed = errors.New("feedKey error")

	// Tenant config errors
	ErrConfigCastFailed      = errors.New("failed to cast config to expected type")
	ErrOriginalOrLoadedNil   = errors.New("original or loaded config is nil")
	ErrDestinationNotPointer = errors.New("destination must be a pointer")
	ErrCannotCopyMapToStruct = errors.New("cannot copy from map to non-struct")
	ErrUnsupportedSourceType = errors.New("unsupported source type")

	// Additional tenant config errors
	ErrTenantSectionConfigNil     = errors.New("tenant section config is nil after feeding")
	ErrCreatedNilProvider         = errors.New("created nil provider for tenant section")
	ErrIncompatibleFieldTypes     = errors.New("incompatible types for field assignment")
	ErrIncompatibleInterfaceValue = errors.New("incompatible interface value for field")
)

Application errors

View Source
var ConfigFeeders = []Feeder{
	feeders.EnvFeeder{},
}

ConfigFeeders provides a default set of configuration feeders for common use cases

Functions ¶

func GenerateSampleConfig ¶ added in v1.2.2

func GenerateSampleConfig(cfg interface{}, format string) ([]byte, error)

GenerateSampleConfig generates a sample configuration for a config struct The format parameter can be "yaml", "json", or "toml"

func LoadTenantConfigs ¶ added in v1.1.0

func LoadTenantConfigs(app Application, tenantService TenantService, params TenantConfigParams) error

LoadTenantConfigs scans the given directory for config files. Each file should be named with the tenant ID (e.g. "tenant123.json"). For each file, it unmarshals the configuration and registers it with the provided TenantService for the given section. The configNameRegex is a regex pattern for the config file names (e.g. "^tenant[0-9]+\\.json$").

func ProcessConfigDefaults ¶ added in v1.2.2

func ProcessConfigDefaults(cfg interface{}) error

ProcessConfigDefaults applies default values to a config struct based on struct tags It looks for `default:"value"` tags on struct fields and sets the field value if currently zero/empty

func SaveSampleConfig ¶ added in v1.2.2

func SaveSampleConfig(cfg interface{}, format, filePath string) error

SaveSampleConfig generates and saves a sample configuration file

func ValidateConfig ¶ added in v1.2.2

func ValidateConfig(cfg interface{}) error

ValidateConfig validates a configuration using the following steps: 1. Processes default values 2. Validates required fields 3. If the config implements ConfigValidator, calls its Validate method

func ValidateConfigRequired ¶ added in v1.2.2

func ValidateConfigRequired(cfg interface{}) error

ValidateConfigRequired checks all struct fields with `required:"true"` tag and verifies they are not zero/empty values

Types ¶

type AppRegistry ¶

type AppRegistry interface {
	// SvcRegistry retrieves the service svcRegistry
	SvcRegistry() ServiceRegistry
}

AppRegistry provides registry functionality for applications

type Application ¶

type Application interface {
	// ConfigProvider retrieves the application config provider
	ConfigProvider() ConfigProvider
	// SvcRegistry retrieves the service svcRegistry
	SvcRegistry() ServiceRegistry
	// RegisterModule adds a module to the application
	RegisterModule(module Module)
	// RegisterConfigSection registers a configuration section with the application
	RegisterConfigSection(section string, cp ConfigProvider)
	// ConfigSections retrieves all registered configuration sections
	ConfigSections() map[string]ConfigProvider
	// GetConfigSection retrieves a configuration section
	GetConfigSection(section string) (ConfigProvider, error)
	// RegisterService adds a service with type checking
	RegisterService(name string, service any) error
	// GetService retrieves a service with type assertion
	GetService(name string, target any) error
	// Init initializes the application with the provided modules
	Init() error
	// Start starts the application
	Start() error
	// Stop stops the application
	Stop() error
	// Run starts the application and blocks until termination
	Run() error
	// Logger retrieves the application's logger
	Logger() Logger
}

Application represents the core application interface with configuration, module management, and service registration

func NewStdApplication ¶ added in v1.1.0

func NewStdApplication(cp ConfigProvider, logger Logger) Application

NewStdApplication creates a new application instance

type ComplexFeeder ¶

type ComplexFeeder interface {
	Feeder
	FeedKey(string, interface{}) error
}

ComplexFeeder extends the basic Feeder interface with additional functionality for complex configuration scenarios

type Config ¶

type Config struct {
	*config.Config
	StructKeys map[string]interface{}
}

Config represents a configuration builder that can combine multiple feeders and structures

func NewConfig ¶

func NewConfig() *Config

NewConfig creates a new configuration builder

func (*Config) AddStructKey ¶

func (c *Config) AddStructKey(key string, target interface{}) *Config

AddStructKey adds a structure with a key to the configuration

func (*Config) Feed ¶

func (c *Config) Feed() error

Feed with validation applies defaults and validates configs after feeding

type ConfigProvider ¶

type ConfigProvider interface {
	// GetConfig returns the configuration object
	GetConfig() any
}

ConfigProvider defines the interface for providing configuration objects

type ConfigSetup ¶ added in v1.0.0

type ConfigSetup interface {
	Setup() error
}

ConfigSetup is an interface that configs can implement to perform additional setup after being populated by feeders

type ConfigValidator ¶ added in v1.2.2

type ConfigValidator interface {
	// Validate validates the configuration and returns an error if invalid
	Validate() error
}

ConfigValidator is an interface for configuration validation

type Configurable ¶ added in v1.2.0

type Configurable interface {
	// RegisterConfig registers configuration requirements
	RegisterConfig(app Application) error
}

Configurable is an interface for modules that can have configuration

type Constructable ¶ added in v1.2.0

type Constructable interface {
	// Constructor returns a function to construct this module
	Constructor() ModuleConstructor
}

Constructable is an interface for modules that can be constructed with a constructor

type DependencyAware ¶ added in v1.2.0

type DependencyAware interface {
	// Dependencies returns names of other modules this module depends on
	Dependencies() []string
}

DependencyAware is an interface for modules that can have dependencies

type Feeder ¶

type Feeder = config.Feeder

Feeder aliases

type FileBasedTenantConfigLoader ¶ added in v1.1.0

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

FileBasedTenantConfigLoader implements TenantConfigLoader for file-based tenant configurations

func DefaultTenantConfigLoader ¶ added in v1.1.0

func DefaultTenantConfigLoader(configDir string) *FileBasedTenantConfigLoader

DefaultTenantConfigLoader creates a loader with default configuration

func NewFileBasedTenantConfigLoader ¶ added in v1.1.0

func NewFileBasedTenantConfigLoader(params TenantConfigParams) *FileBasedTenantConfigLoader

NewFileBasedTenantConfigLoader creates a new file-based tenant config loader

func (*FileBasedTenantConfigLoader) LoadTenantConfigurations ¶ added in v1.1.0

func (l *FileBasedTenantConfigLoader) LoadTenantConfigurations(app Application, tenantService TenantService) error

LoadTenantConfigurations loads tenant configurations from files

type LoadAppConfigFunc ¶ added in v1.0.0

type LoadAppConfigFunc func(*StdApplication) error

LoadAppConfigFunc is the function type for loading application configuration

var AppConfigLoader LoadAppConfigFunc = loadAppConfig

AppConfigLoader is the default implementation that can be replaced in tests

type Logger ¶

type Logger interface {
	Info(msg string, args ...any)
	Error(msg string, args ...any)
	Warn(msg string, args ...any)
	Debug(msg string, args ...any)
}

Logger defines the interface for application logging

type Module ¶

type Module interface {
	// Name returns the unique identifier for this module
	Name() string
	// Init Initialize the module with the application context
	Init(app Application) error
}

Module represents a registrable component in the application

type ModuleConstructor ¶

type ModuleConstructor func(app Application, services map[string]any) (Module, error)

ModuleConstructor is a function type that creates module instances with dependency injection

type ModuleRegistry ¶

type ModuleRegistry map[string]Module

ModuleRegistry represents a svcRegistry of modules

type ModuleWithConstructor ¶

type ModuleWithConstructor interface {
	Module
	Constructable
}

ModuleWithConstructor defines modules that support constructor-based dependency injection

type ServiceAware ¶ added in v1.2.0

type ServiceAware interface {
	// ProvidesServices returns a list of services provided by this module
	ProvidesServices() []ServiceProvider
	// RequiresServices returns a list of services required by this module
	RequiresServices() []ServiceDependency
}

ServiceAware is an interface for modules that can provide or require services

type ServiceDependency ¶

type ServiceDependency struct {
	Name               string       // Service name to lookup (can be empty for interface-based lookup)
	Required           bool         // If true, application fails to start if service is missing
	Type               reflect.Type // Concrete type (if known)
	SatisfiesInterface reflect.Type // Interface type (if known)
	MatchByInterface   bool         // If true, find first service that satisfies interface type
}

ServiceDependency defines a dependency on a service

type ServiceProvider ¶ added in v1.0.0

type ServiceProvider struct {
	Name        string
	Description string
	Instance    any
}

ServiceProvider defines a service with metadata

type ServiceRegistry ¶

type ServiceRegistry map[string]any

ServiceRegistry allows registration and retrieval of services

type StandardTenantService ¶ added in v1.1.0

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

StandardTenantService provides a basic implementation of the TenantService interface

func NewStandardTenantService ¶ added in v1.1.0

func NewStandardTenantService(logger Logger) *StandardTenantService

NewStandardTenantService creates a new tenant service

func (*StandardTenantService) GetTenantConfig ¶ added in v1.1.0

func (ts *StandardTenantService) GetTenantConfig(tenantID TenantID, section string) (ConfigProvider, error)

GetTenantConfig retrieves tenant-specific configuration

func (*StandardTenantService) GetTenants ¶ added in v1.1.0

func (ts *StandardTenantService) GetTenants() []TenantID

GetTenants returns all registered tenant IDs

func (*StandardTenantService) RegisterTenant ¶ added in v1.1.0

func (ts *StandardTenantService) RegisterTenant(tenantID TenantID, configs map[string]ConfigProvider) error

RegisterTenant registers a new tenant with optional initial configs

func (*StandardTenantService) RegisterTenantAwareModule ¶ added in v1.1.0

func (ts *StandardTenantService) RegisterTenantAwareModule(module TenantAwareModule) error

RegisterTenantAwareModule registers a module to receive tenant events

func (*StandardTenantService) RegisterTenantConfigSection ¶ added in v1.1.0

func (ts *StandardTenantService) RegisterTenantConfigSection(tenantID TenantID, section string, provider ConfigProvider) error

RegisterTenantConfigSection registers a configuration section for a specific tenant

func (*StandardTenantService) RemoveTenant ¶ added in v1.1.0

func (ts *StandardTenantService) RemoveTenant(tenantID TenantID) error

RemoveTenant removes a tenant and its configurations

type Startable ¶ added in v1.2.0

type Startable interface {
	Start(ctx context.Context) error
}

Startable is an interface for modules that can be started

type StdApplication ¶ added in v1.1.0

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

StdApplication represents the core StdApplication container

func (*StdApplication) ConfigProvider ¶ added in v1.1.0

func (app *StdApplication) ConfigProvider() ConfigProvider

ConfigProvider retrieves the application config provider

func (*StdApplication) ConfigSections ¶ added in v1.1.0

func (app *StdApplication) ConfigSections() map[string]ConfigProvider

ConfigSections retrieves all registered configuration sections

func (*StdApplication) GetConfigSection ¶ added in v1.1.0

func (app *StdApplication) GetConfigSection(section string) (ConfigProvider, error)

GetConfigSection retrieves a configuration section

func (*StdApplication) GetService ¶ added in v1.1.0

func (app *StdApplication) GetService(name string, target any) error

GetService retrieves a service with type assertion

func (*StdApplication) GetTenantConfig ¶ added in v1.1.0

func (app *StdApplication) GetTenantConfig(tenantID TenantID, section string) (ConfigProvider, error)

GetTenantConfig retrieves configuration for a specific tenant and section

func (*StdApplication) GetTenantService ¶ added in v1.1.0

func (app *StdApplication) GetTenantService() (TenantService, error)

GetTenantService returns the application's tenant service if available

func (*StdApplication) Init ¶ added in v1.1.0

func (app *StdApplication) Init() error

Init initializes the application with the provided modules

func (*StdApplication) Logger ¶ added in v1.1.0

func (app *StdApplication) Logger() Logger

Logger represents a logger

func (*StdApplication) RegisterConfigSection ¶ added in v1.1.0

func (app *StdApplication) RegisterConfigSection(section string, cp ConfigProvider)

RegisterConfigSection registers a configuration section with the application

func (*StdApplication) RegisterModule ¶ added in v1.1.0

func (app *StdApplication) RegisterModule(module Module)

RegisterModule adds a module to the application

func (*StdApplication) RegisterService ¶ added in v1.1.0

func (app *StdApplication) RegisterService(name string, service any) error

RegisterService adds a service with type checking

func (*StdApplication) Run ¶ added in v1.1.0

func (app *StdApplication) Run() error

Run starts the application and blocks until termination

func (*StdApplication) Start ¶ added in v1.1.0

func (app *StdApplication) Start() error

Start starts the application

func (*StdApplication) Stop ¶ added in v1.1.0

func (app *StdApplication) Stop() error

Stop stops the application

func (*StdApplication) SvcRegistry ¶ added in v1.1.0

func (app *StdApplication) SvcRegistry() ServiceRegistry

SvcRegistry retrieves the service svcRegistry

func (*StdApplication) WithTenant ¶ added in v1.1.0

func (app *StdApplication) WithTenant(tenantID TenantID) (*TenantContext, error)

WithTenant creates a tenant context from the application context

type StdConfigProvider ¶

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

StdConfigProvider provides a standard implementation of ConfigProvider

func NewStdConfigProvider ¶

func NewStdConfigProvider(cfg any) *StdConfigProvider

NewStdConfigProvider creates a new standard configuration provider

func (*StdConfigProvider) GetConfig ¶

func (s *StdConfigProvider) GetConfig() any

GetConfig returns the configuration object

type Stoppable ¶ added in v1.2.0

type Stoppable interface {
	Stop(ctx context.Context) error
}

Stoppable is an interface for modules that can be stopped

type TenantApplication ¶ added in v1.1.0

type TenantApplication interface {
	Application
	// GetTenantService returns the application's tenant service if available
	GetTenantService() (TenantService, error)
	// WithTenant creates a tenant context from the application context
	WithTenant(tenantID TenantID) (*TenantContext, error)
	// GetTenantConfig retrieves configuration for a specific tenant and section
	GetTenantConfig(tenantID TenantID, section string) (ConfigProvider, error)
}

TenantApplication extends Application with multi-tenant functionality

type TenantAwareConfig ¶ added in v1.1.0

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

TenantAwareConfig provides configuration that's aware of tenant context

func NewTenantAwareConfig ¶ added in v1.1.0

func NewTenantAwareConfig(defaultConfig ConfigProvider, tenantService TenantService, configSection string) *TenantAwareConfig

NewTenantAwareConfig creates a new tenant-aware configuration provider

func (*TenantAwareConfig) GetConfig ¶ added in v1.1.0

func (tac *TenantAwareConfig) GetConfig() any

GetConfig retrieves the default configuration when no tenant is specified

func (*TenantAwareConfig) GetConfigWithContext ¶ added in v1.1.0

func (tac *TenantAwareConfig) GetConfigWithContext(ctx context.Context) any

GetConfigWithContext retrieves tenant-specific configuration based on context

type TenantAwareModule ¶ added in v1.1.0

type TenantAwareModule interface {
	Module

	// OnTenantRegistered is called when a new tenant is registered
	OnTenantRegistered(tenantID TenantID)

	// OnTenantRemoved is called when a tenant is removed
	OnTenantRemoved(tenantID TenantID)
}

TenantAwareModule is an optional interface that modules can implement to receive notifications about tenant lifecycle events

type TenantAwareRegistry ¶ added in v1.1.0

type TenantAwareRegistry interface {
	// GetServiceForTenant returns a service instance for a specific tenant
	GetServiceForTenant(name string, tenantID TenantID, target any) error
}

TenantAwareRegistry provides common service discovery methods that are tenant-aware

type TenantConfigLoader ¶ added in v1.1.0

type TenantConfigLoader interface {
	// LoadTenantConfigurations loads configurations for all tenants
	LoadTenantConfigurations(app Application, tenantService TenantService) error
}

TenantConfigLoader is an interface for loading tenant configurations

type TenantConfigParams ¶ added in v1.1.0

type TenantConfigParams struct {
	// ConfigNameRegex is a regex pattern for the config file names (e.g. "^tenant[0-9]+\\.json$").
	ConfigNameRegex *regexp.Regexp
	// ConfigDir is the directory where tenant config files are located.
	ConfigDir string
	// ConfigFeeders are the feeders to use for loading tenant configs.
	ConfigFeeders []Feeder
}

TenantConfigParams defines parameters for loading tenant configurations

type TenantConfigProvider ¶ added in v1.1.0

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

TenantConfigProvider manages configurations for multiple tenants

func NewTenantConfigProvider ¶ added in v1.1.0

func NewTenantConfigProvider(defaultConfig ConfigProvider) *TenantConfigProvider

NewTenantConfigProvider creates a new tenant configuration provider

func (*TenantConfigProvider) GetConfig ¶ added in v1.1.0

func (tcp *TenantConfigProvider) GetConfig() any

GetConfig returns the default configuration to satisfy ConfigProvider interface

func (*TenantConfigProvider) GetDefaultConfig ¶ added in v1.1.0

func (tcp *TenantConfigProvider) GetDefaultConfig() ConfigProvider

GetDefaultConfig returns the default configuration (non-tenant specific)

func (*TenantConfigProvider) GetTenantConfig ¶ added in v1.1.0

func (tcp *TenantConfigProvider) GetTenantConfig(tenantID TenantID, section string) (ConfigProvider, error)

GetTenantConfig retrieves a configuration for a specific tenant and section

func (*TenantConfigProvider) HasTenantConfig ¶ added in v1.1.0

func (tcp *TenantConfigProvider) HasTenantConfig(tenantID TenantID, section string) bool

HasTenantConfig checks if a configuration exists for a specific tenant and section

func (*TenantConfigProvider) SetTenantConfig ¶ added in v1.1.0

func (tcp *TenantConfigProvider) SetTenantConfig(tenantID TenantID, section string, provider ConfigProvider)

SetTenantConfig sets a configuration for a specific tenant and section

type TenantContext ¶ added in v1.1.0

type TenantContext struct {
	context.Context
	// contains filtered or unexported fields
}

TenantContext is a context for tenant-aware operations

func NewTenantContext ¶ added in v1.1.0

func NewTenantContext(ctx context.Context, tenantID TenantID) *TenantContext

NewTenantContext creates a new context with tenant information

func (*TenantContext) GetTenantID ¶ added in v1.1.0

func (tc *TenantContext) GetTenantID() TenantID

GetTenantID returns the tenant ID from the context

type TenantID ¶ added in v1.1.0

type TenantID string

TenantID represents a unique tenant identifier

func GetTenantIDFromContext ¶ added in v1.1.0

func GetTenantIDFromContext(ctx context.Context) (TenantID, bool)

GetTenantIDFromContext attempts to extract tenant ID from a context

type TenantService ¶ added in v1.1.0

type TenantService interface {
	// GetTenantConfig returns tenant-specific config for the given tenant and section
	GetTenantConfig(tenantID TenantID, section string) (ConfigProvider, error)

	// GetTenants returns all tenant IDs
	GetTenants() []TenantID

	// RegisterTenant registers a new tenant with optional initial configs
	RegisterTenant(tenantID TenantID, configs map[string]ConfigProvider) error

	// RegisterTenantAwareModule registers a module that wants to be notified about tenant lifecycle events
	RegisterTenantAwareModule(module TenantAwareModule) error
}

TenantService provides tenant management functionality

Directories ¶

Path Synopsis
cmd
modcli module
Package feeders provides configuration feeders for reading data from various sources including environment variables, JSON, YAML, TOML files, and .env files.
Package feeders provides configuration feeders for reading data from various sources including environment variables, JSON, YAML, TOML files, and .env files.
modules
auth module
cache module
chimux module
database module
eventbus module
eventlogger module
httpclient module
httpserver module
jsonschema module
letsencrypt module
logmasker module
reverseproxy module
scheduler module

Jump to

Keyboard shortcuts

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