Update status of all epic0 stories (0.1-0.5) from Pending to Completed: - 0.1: Project Initialization - Directory structure and Go module setup - 0.2: Configuration Management System - Viper-based config implemented - 0.3: Structured Logging System - Zap logger with middleware implemented - 0.4: CI/CD Pipeline - GitHub Actions workflow with tests and linting - 0.5: DI and Bootstrap - FX-based DI container with lifecycle management All stories have been implemented with tests and are working.
5.4 KiB
Story 0.2: Configuration Management System
Metadata
- Story ID: 0.2
- Title: Configuration Management System
- Epic: 0 - Project Setup & Foundation
- Status: Completed
- Priority: High
- Estimated Time: 4-6 hours
- Dependencies: 0.1
Goal
Implement a flexible configuration system that loads settings from YAML files, environment variables, and supports type-safe access. The system must be injectable via DI and usable by all modules.
Description
This story implements a complete configuration management system using Viper that provides a clean interface for accessing configuration values. The system supports multiple configuration sources (YAML files, environment variables) with proper precedence and type-safe accessors.
Deliverables
1. Configuration Interface (pkg/config/config.go)
Define ConfigProvider interface with:
Get(key string) any- Get any valueUnmarshal(v any) error- Unmarshal into structGetString(key string) string- Type-safe string getterGetInt(key string) int- Type-safe int getterGetBool(key string) bool- Type-safe bool getterGetStringSlice(key string) []string- Type-safe slice getterGetDuration(key string) time.Duration- Type-safe duration getterIsSet(key string) bool- Check if key exists
2. Viper Implementation (internal/config/config.go)
Implement ConfigProvider using Viper:
- Load
config/default.yamlas baseline - Merge environment-specific YAML files (development/production)
- Apply environment variable overrides (uppercase with underscores)
- Support nested configuration keys (dot notation)
- Provide all type-safe getters
- Support unmarshaling into structs
- Handle configuration validation errors
3. Configuration Loader (internal/config/loader.go)
LoadConfig(env string) (ConfigProvider, error)function- Environment detection (development/production)
- Configuration file discovery
- Validation of required configuration keys
- Error handling and reporting
4. Configuration Files
Create configuration files:
config/default.yaml - Base configuration:
environment: development
server:
port: 8080
host: "0.0.0.0"
read_timeout: 30s
write_timeout: 30s
database:
driver: "postgres"
dsn: ""
max_connections: 25
max_idle_connections: 5
logging:
level: "info"
format: "json"
output: "stdout"
config/development.yaml - Development overrides:
environment: development
logging:
level: "debug"
format: "console"
config/production.yaml - Production overrides:
environment: production
logging:
level: "warn"
format: "json"
5. DI Integration
- Provider function for ConfigProvider
- Register in DI container
- Make configurable via FX
Implementation Steps
-
Install Dependencies
go get github.com/spf13/viper@v1.18.0 go get github.com/spf13/cobra@v1.8.0 -
Create Configuration Interface
- Define
ConfigProviderinterface inpkg/config/config.go - Add package documentation
- Export interface for use by modules
- Define
-
Implement Viper Configuration
- Create
internal/config/config.go - Implement all interface methods
- Handle configuration loading and merging
- Support nested keys and environment variables
- Create
-
Create Configuration Loader
- Implement
LoadConfig()function - Add environment detection logic
- Add validation logic
- Handle errors gracefully
- Implement
-
Create Configuration Files
- Create
config/default.yamlwith base configuration - Create
config/development.yamlwith dev overrides - Create
config/production.yamlwith prod overrides - Ensure proper YAML structure
- Create
-
Integrate with DI
- Create provider function
- Register in DI container
- Test injection
Acceptance Criteria
ConfigProviderinterface is defined and documented- Viper implementation loads YAML files successfully
- Environment variables override YAML values
- Type-safe getters work correctly (string, int, bool, etc.)
- Configuration can be unmarshaled into structs
- Nested keys work with dot notation
- Configuration system is injectable via DI container
- All modules can access configuration through interface
- Configuration validation works
- Error handling is comprehensive
Related ADRs
Implementation Notes
- Use Viper's automatic environment variable support (uppercase with underscores)
- Support both single-level and nested configuration keys
- Consider adding configuration schema validation in future
- Environment variable format:
SERVER_PORT,DATABASE_DSN, etc. - Configuration files should be in YAML for readability
- Support secret manager integration placeholder (Epic 6)
Testing
# Test configuration loading
go test ./internal/config/...
# Test type-safe getters
go run -c "test config getters"
# Test environment variable overrides
export SERVER_PORT=9090
go run cmd/platform/main.go
Files to Create/Modify
pkg/config/config.go- Configuration interfaceinternal/config/config.go- Viper implementationinternal/config/loader.go- Configuration loaderconfig/default.yaml- Base configurationconfig/development.yaml- Development configurationconfig/production.yaml- Production configurationinternal/di/providers.go- Add config provider