# Modular Configuration System This package provides a modular configuration system for the Tender Management backend. It allows each command (web, scraper, worker, etc.) to have its own specific configuration while sharing common base configurations. ## Architecture ### Base Configuration (`BaseConfig`) The `BaseConfig` struct contains common configuration fields that are used across all commands: - **Server**: HTTP server configuration (host, port, timeouts) - **Database**: MongoDB connection configuration - **Cache**: Redis connection configuration - **Logging**: Logging system configuration ### Command-Specific Configuration Each command can extend the `BaseConfig` by embedding it and adding its own specific configuration fields. ## Usage ### 1. Create Command-Specific Config Create a config file for your command (e.g., `cmd/yourcommand/config.go`): ```go package main import ( "time" "tm/pkg/config" ) // Config holds configuration for your command type Config struct { config.BaseConfig YourSpecific YourSpecificConfig `mapstructure:"your_specific"` } type YourSpecificConfig struct { SomeField string `mapstructure:"some_field"` SomeTimeout time.Duration `mapstructure:"some_timeout"` SomeNumber int `mapstructure:"some_number"` } ``` ### 2. Load Configuration In your command's `main.go` or bootstrap file: ```go func initConfig() Config { conf, err := config.LoadConfig(".", &Config{}) if err != nil { panic(fmt.Sprintf("Failed to load config: %v", err)) } return *conf } ``` ### 3. Create YAML Configuration File Create a `config.yaml` file in your command directory: ```yaml # Base configuration (common to all commands) server: host: "localhost" port: 8080 timeout: 30s read_timeout: 10s write_timeout: 10s database: mongodb: uri: "mongodb://localhost:27017" name: "your_database" timeout: 10s max_pool_size: 100 cache: redis: host: "localhost" port: 6379 password: "" db: 0 pool_size: 10 logging: level: "info" format: "json" output: "stdout" file: path: "/var/log/tm/your-command.log" max_size: 100 max_backups: 5 max_age: 30 compress: true # Your command-specific configuration your_specific: some_field: "value" some_timeout: 30s some_number: 42 ``` ## Examples ### Web Command Config ```go type Config struct { config.BaseConfig Queue QueueConfig `mapstructure:"queue"` UserAuthorization AuthConfig `mapstructure:"user_authorization"` CustomerAuthorization AuthConfig `mapstructure:"customer_authorization"` AI AIConfig `mapstructure:"ai"` RateLimit RateLimitConfig `mapstructure:"rate_limiting"` } ``` ### Scraper Command Config ```go type Config struct { config.BaseConfig TED TEDConfig `mapstructure:"ted"` } type TEDConfig struct { BaseURL string `mapstructure:"base_url"` MaxRetries int `mapstructure:"max_retries"` MaxConcurrency int `mapstructure:"max_concurrency"` DownloadDir string `mapstructure:"download_dir"` CleanupAfter time.Duration `mapstructure:"cleanup_after"` } ``` ### Worker Command Config ```go type Config struct { config.BaseConfig Queue QueueConfig `mapstructure:"queue"` AI AIConfig `mapstructure:"ai"` Worker WorkerConfig `mapstructure:"worker"` Scraping ScrapingConfig `mapstructure:"scraping"` } ``` ## Features ### Type Safety The configuration loader uses Go generics to provide type-safe configuration loading. The function signature ensures that you get back exactly the type you expect. ### Environment Variable Support The configuration system automatically supports environment variable overrides via Viper's `AutomaticEnv()` feature. ### YAML Support Configuration files use YAML format for easy readability and maintenance. ### Validation Each command can implement its own validation logic after loading the configuration. ## Best Practices 1. **Keep BaseConfig minimal**: Only add fields to `BaseConfig` that are truly common across all commands. 2. **Use descriptive names**: Choose clear, descriptive names for your configuration structs and fields. 3. **Document your config**: Add comments to your configuration structs explaining what each field does. 4. **Use appropriate types**: Use `time.Duration` for durations, proper numeric types for numbers, etc. 5. **Provide defaults**: Consider providing sensible defaults in your YAML files. 6. **Environment variables**: Use environment variables for sensitive data like API keys and passwords. ## Migration from Old System If you're migrating from the old `infra.Config` system: 1. Remove imports of `tm/infra` 2. Add imports of `tm/pkg/config` 3. Change your config struct to embed `config.BaseConfig` 4. Update your `LoadConfig` call to use the new generic function 5. Update function signatures to use the new config types Example migration: ```go // Old way func initConfig() infra.Config { config, err := infra.LoadConfig(".") if err != nil { panic(fmt.Sprintf("Failed to load config: %v", err)) } return *config } // New way func initConfig() Config { conf, err := config.LoadConfig(".", &Config{}) if err != nil { panic(fmt.Sprintf("Failed to load config: %v", err)) } return *conf } ```