Skip to content

Latest commit

 

History

History
282 lines (207 loc) · 7.05 KB

app-lifecycle.md

File metadata and controls

282 lines (207 loc) · 7.05 KB

App Lifecycle Management

This document explains the complete App lifecycle in host-http framework and how to implement proper service initialization using the ServiceProvider builder pattern.

Overview

The host-http framework, built on top of github.com/Bofry/host, provides a complete application lifecycle management system. This allows you to properly initialize, configure, and cleanup your application and its services.

Lifecycle Methods

The framework recognizes and calls the following methods on your App struct (if implemented):

1. Init()

Purpose: Basic app initialization When Called: First method called during startup Use Case: Initialize basic components that don't depend on configuration

func (app *App) Init() {
    // Basic initialization
    fmt.Printf("App starting up...\n")
}

2. OnInit()

Purpose: Configuration and service setup When Called: After Init(), with configuration loaded Use Case: Initialize ServiceProvider and configure services

func (app *App) OnInit() {
    // Initialize ServiceProvider with dependency injection
    if app.ServiceProvider != nil {
        app.ServiceProvider.Init(app.Config, app)
    }
}

3. OnInitComplete()

Purpose: Finalization after all initialization is complete When Called: After all components are initialized Use Case: Final setup steps, validation

func (app *App) OnInitComplete() {
    fmt.Printf("All initialization completed\n")
}

4. OnStart(ctx context.Context)

Purpose: Server startup notification When Called: When the HTTP server starts listening Use Case: Register runtime services, log startup information

func (app *App) OnStart(ctx context.Context) {
    fmt.Printf("Server started on %s\n", app.Config.ListenAddress)
}

5. OnStop(ctx context.Context)

Purpose: Graceful shutdown When Called: When the server receives shutdown signal Use Case: Cleanup resources, stop services gracefully

func (app *App) OnStop(ctx context.Context) {
    if app.ServiceProvider != nil {
        app.ServiceProvider.Stop()
    }
}

Method Execution Order

The framework calls these methods in the following order:

  1. ConfigureLogger() (if implemented) - Configure application logger
  2. OnInit() - Load configuration and initialize services
  3. Init() - Basic app initialization
  4. OnInitComplete() - Finalization
  5. OnStart(ctx) - Server startup
  6. [Server runs]
  7. OnStop(ctx) - Graceful shutdown (on SIGTERM/SIGINT)

ServiceProvider Builder Pattern

The ServiceProvider implements the builder pattern for dependency injection and service management, similar to the reference project pattern.

Basic ServiceProvider Structure

type ServiceProvider struct {
    services map[string]interface{}
    mutex    sync.RWMutex
    config   *Config
}

// Init initializes services in dependency order
func (sp *ServiceProvider) Init(config *Config, app *App) {
    sp.config = config

    // Initialize services in correct order
    sp.initCoreServices(config)
    sp.initApplicationServices(config)
    sp.initUtilityServices(config)
}

// Stop gracefully stops all services
func (sp *ServiceProvider) Stop() {
    // Stop services in reverse dependency order
    sp.stopServices()
}

Service Management Methods

// RegisterService registers a service
func (sp *ServiceProvider) RegisterService(name string, service interface{}) {
    sp.mutex.Lock()
    defer sp.mutex.Unlock()
    sp.services[name] = service
}

// GetService retrieves a service
func (sp *ServiceProvider) GetService(name string) interface{} {
    sp.mutex.RLock()
    defer sp.mutex.RUnlock()
    return sp.services[name]
}

App Structure Template

Here's a complete App structure template:

type App struct {
    Host            *hosthttp.Host   `host:""`
    Config          *Config          `config:""`
    ServiceProvider *ServiceProvider `serviceProvider:""`
}

type Config struct {
    ListenAddress  string `yaml:"ListenAddress" env:"HTTP_LISTEN_ADDRESS" default:":8080"`
    EnableCompress bool   `yaml:"EnableCompress" env:"ENABLE_COMPRESS" default:"false"`
    ServerName     string `yaml:"ServerName" env:"SERVER_NAME" default:"host-http-app"`
}

type ServiceProvider struct {
    services map[string]interface{}
    mutex    sync.RWMutex
    config   *Config
}

Best Practices

1. Dependency Order

Initialize services in the correct dependency order:

  • Core infrastructure services first (config, logging)
  • Application services second (business logic)
  • Utility services last (caching, monitoring)

2. Error Handling

Handle errors gracefully during initialization:

func (sp *ServiceProvider) Init(config *Config, app *App) {
    if err := sp.initCoreServices(config); err != nil {
        log.Fatalf("Failed to initialize core services: %v", err)
    }
}

3. Graceful Shutdown

Stop services in reverse dependency order:

func (sp *ServiceProvider) Stop() {
    // Stop in reverse order
    sp.stopUtilityServices()
    sp.stopApplicationServices()
    sp.stopCoreServices()
}

4. Configuration Management

Use environment variables with defaults:

type Config struct {
    DatabaseURL string `env:"DATABASE_URL" required:"true"`
    Port        string `env:"PORT" default:"8080"`
    LogLevel    string `env:"LOG_LEVEL" default:"info"`
}

Examples

Complete Example

See _examples/basic/app-lifecycle/ for a complete implementation demonstrating:

  • Full lifecycle method implementation
  • ServiceProvider builder pattern
  • Service registration and management
  • Graceful shutdown handling

Simple Example

See _examples/basic/health-check/ for a simple implementation with basic lifecycle methods.

Troubleshooting

Methods Not Called

If lifecycle methods aren't being called:

  1. Ensure methods have the correct signature
  2. Check that your App struct has the correct field tags
  3. Verify the Bofry/host framework version compatibility

ServiceProvider Issues

If ServiceProvider methods aren't triggering:

  1. Ensure OnInit() calls ServiceProvider.Init()
  2. Ensure OnStop() calls ServiceProvider.Stop()
  3. Check for nil pointer access

Port Binding Issues

If you get "address already in use" errors:

# Kill processes using the port
lsof -ti:8080 | xargs -r kill -9

# Or use different ports
HTTP_LISTEN_ADDRESS=:9090 go run .

Environment Variables

Common environment variables for configuration:

Variable Description Default
HTTP_LISTEN_ADDRESS Server listen address :8080
ENABLE_COMPRESS Enable HTTP compression false
SERVER_NAME Server identification host-http-app
LOG_LEVEL Logging level info

Integration with Framework

The host-http framework integrates with Bofry/host using:

  • Dependency injection for App components
  • Automatic configuration loading
  • Service lifecycle management
  • Graceful shutdown handling

This provides a robust foundation for building production-ready HTTP applications with proper service management and cleanup.