backend-service/internal/container/container.go

341 lines
9.9 KiB
Go
Raw Permalink Normal View History

package container
import (
"fmt"
"attune-heart-therapy/internal/config"
"attune-heart-therapy/internal/database"
"attune-heart-therapy/internal/handlers"
"attune-heart-therapy/internal/logger"
"attune-heart-therapy/internal/repositories"
"attune-heart-therapy/internal/services"
)
// Container holds all application dependencies
type Container struct {
Config *config.Config
Database *database.DB
Log *logger.Logger
// Repositories
Repositories *repositories.Repositories
// Services
JWTService services.JWTService
UserService services.UserService
BookingService services.BookingService
PaymentService services.PaymentService
NotificationService services.NotificationService
JitsiService services.JitsiService
AdminService services.AdminService
JobManagerService services.JobManagerService
// Handlers
AuthHandler *handlers.AuthHandler
BookingHandler *handlers.BookingHandler
PaymentHandler *handlers.PaymentHandler
AdminHandler *handlers.AdminHandler
MeetingHandler *handlers.MeetingHandler
}
// New creates a new dependency injection container
func New(cfg *config.Config) *Container {
return &Container{
Config: cfg,
Log: logger.New("container"),
}
}
// Initialize sets up all dependencies in the correct order
func (c *Container) Initialize() error {
c.Log.Info("Initializing application dependencies...")
// Initialize database connection
if err := c.initializeDatabase(); err != nil {
return fmt.Errorf("failed to initialize database: %w", err)
}
// Initialize repositories
c.initializeRepositories()
// Initialize services
if err := c.initializeServices(); err != nil {
return fmt.Errorf("failed to initialize services: %w", err)
}
// Initialize handlers
c.initializeHandlers()
c.Log.Info("Application dependencies initialized successfully")
return nil
}
// initializeDatabase sets up database connection and runs migrations
func (c *Container) initializeDatabase() error {
c.Log.Info("Initializing database connection...")
db, err := database.New(c.Config)
if err != nil {
return fmt.Errorf("failed to create database connection: %w", err)
}
c.Database = db
// Run database migrations
c.Log.Info("Running database migrations...")
if err := c.Database.Migrate(); err != nil {
return fmt.Errorf("failed to run database migrations: %w", err)
}
// Seed database with initial data
c.Log.Info("Seeding database with initial data...")
if err := c.Database.Seed(); err != nil {
return fmt.Errorf("failed to seed database: %w", err)
}
c.Log.Info("Database initialization completed")
return nil
}
// initializeRepositories sets up all repository instances
func (c *Container) initializeRepositories() {
c.Log.Info("Initializing repositories...")
c.Repositories = c.Database.GetRepositories()
c.Log.Info("Repositories initialized")
}
// initializeServices sets up all service instances with proper dependency injection
func (c *Container) initializeServices() error {
c.Log.Info("Initializing services...")
// Initialize JWT service (no dependencies)
c.JWTService = services.NewJWTService(c.Config.JWT.Secret, c.Config.JWT.Expiration)
// Initialize Jitsi service (no dependencies)
c.JitsiService = services.NewJitsiService(&c.Config.Jitsi)
// Initialize notification service (depends on notification repository, config, and jitsi service)
c.NotificationService = services.NewNotificationService(c.Repositories.Notification, c.Config, c.JitsiService)
// Initialize user service (depends on user repository, JWT service, and notification service)
c.UserService = services.NewUserService(c.Repositories.User, c.JWTService, c.NotificationService)
// Initialize payment service (depends on config, booking/user repositories, and notification service)
c.PaymentService = services.NewPaymentService(c.Config, c.Repositories.Booking, c.Repositories.User, c.NotificationService)
// Initialize job manager service (depends on notification service and repositories)
c.JobManagerService = services.NewJobManagerService(c.NotificationService, c.Repositories.Booking, c.Repositories.User)
// Start the job manager
c.Log.Info("Starting job manager...")
if err := c.JobManagerService.Start(); err != nil {
return fmt.Errorf("failed to start job manager: %w", err)
}
c.Log.Info("Job manager started successfully")
// Initialize booking service (depends on multiple repositories and services)
c.BookingService = services.NewBookingService(
c.Repositories.Booking,
c.Repositories.Schedule,
c.Repositories.User,
c.JitsiService,
c.PaymentService,
c.NotificationService,
c.JobManagerService,
)
// Initialize admin service (depends on repositories)
c.AdminService = services.NewAdminService(c.Repositories.User, c.Repositories.Booking, c.Repositories.Schedule)
c.Log.Info("Services initialized successfully")
return nil
}
// initializeHandlers sets up all HTTP handlers with service dependencies
func (c *Container) initializeHandlers() {
c.Log.Info("Initializing handlers...")
c.AuthHandler = handlers.NewAuthHandler(c.UserService)
c.BookingHandler = handlers.NewBookingHandler(c.BookingService, c.Repositories.User, c.JitsiService)
c.PaymentHandler = handlers.NewPaymentHandler(c.PaymentService)
c.AdminHandler = handlers.NewAdminHandler(c.AdminService, c.Repositories.User, c.JitsiService)
c.MeetingHandler = handlers.NewMeetingHandler(c.Repositories.Booking, c.Repositories.User, c.JitsiService)
c.Log.Info("Handlers initialized successfully")
}
// Shutdown gracefully shuts down all services and connections
func (c *Container) Shutdown() error {
c.Log.Info("Shutting down application dependencies...")
var shutdownErrors []error
// Stop job manager first
if c.JobManagerService != nil && c.JobManagerService.IsRunning() {
c.Log.Info("Stopping job manager...")
if err := c.JobManagerService.Stop(); err != nil {
c.Log.Error("Error stopping job manager", err)
shutdownErrors = append(shutdownErrors, fmt.Errorf("job manager shutdown error: %w", err))
} else {
c.Log.Info("Job manager stopped successfully")
}
}
// Close database connection
if c.Database != nil {
c.Log.Info("Closing database connection...")
if err := c.Database.Close(); err != nil {
c.Log.Error("Error closing database", err)
shutdownErrors = append(shutdownErrors, fmt.Errorf("database shutdown error: %w", err))
} else {
c.Log.Info("Database connection closed successfully")
}
}
if len(shutdownErrors) > 0 {
return fmt.Errorf("shutdown completed with errors: %v", shutdownErrors)
}
c.Log.Info("Application dependencies shutdown completed successfully")
return nil
}
// GetJWTService returns the JWT service for middleware usage
func (c *Container) GetJWTService() services.JWTService {
return c.JWTService
}
// HealthCheck performs a health check on all critical dependencies
func (c *Container) HealthCheck() map[string]interface{} {
health := map[string]interface{}{
"status": "ok",
}
// Check database connectivity
if c.Database != nil {
if err := c.Database.Health(); err != nil {
health["database"] = map[string]interface{}{
"status": "error",
"error": err.Error(),
}
health["status"] = "degraded"
} else {
health["database"] = map[string]interface{}{
"status": "healthy",
}
}
} else {
health["database"] = map[string]interface{}{
"status": "not_initialized",
}
health["status"] = "error"
}
// Check job manager status
if c.JobManagerService != nil {
isRunning := c.JobManagerService.IsRunning()
health["job_manager"] = map[string]interface{}{
"status": "healthy",
"running": isRunning,
}
if !isRunning {
health["job_manager"].(map[string]interface{})["status"] = "unhealthy"
health["status"] = "degraded"
}
} else {
health["job_manager"] = map[string]interface{}{
"status": "not_initialized",
}
health["status"] = "degraded"
}
// Check services initialization
servicesHealth := map[string]interface{}{}
if c.UserService != nil {
servicesHealth["user_service"] = "initialized"
} else {
servicesHealth["user_service"] = "not_initialized"
health["status"] = "degraded"
}
if c.BookingService != nil {
servicesHealth["booking_service"] = "initialized"
} else {
servicesHealth["booking_service"] = "not_initialized"
health["status"] = "degraded"
}
if c.PaymentService != nil {
servicesHealth["payment_service"] = "initialized"
} else {
servicesHealth["payment_service"] = "not_initialized"
health["status"] = "degraded"
}
if c.NotificationService != nil {
servicesHealth["notification_service"] = "initialized"
} else {
servicesHealth["notification_service"] = "not_initialized"
health["status"] = "degraded"
}
health["services"] = servicesHealth
return health
}
// IsInitialized returns true if the container has been initialized
func (c *Container) IsInitialized() bool {
return c.Database != nil && c.Repositories != nil && c.UserService != nil
}
// GetDependencyStatus returns the status of all dependencies
func (c *Container) GetDependencyStatus() map[string]string {
status := make(map[string]string)
// Database status
if c.Database != nil {
if err := c.Database.Health(); err != nil {
status["database"] = "unhealthy"
} else {
status["database"] = "healthy"
}
} else {
status["database"] = "not_initialized"
}
// Services status
services := []struct {
name string
service interface{}
}{
{"user_service", c.UserService},
{"booking_service", c.BookingService},
{"payment_service", c.PaymentService},
{"notification_service", c.NotificationService},
{"jitsi_service", c.JitsiService},
{"admin_service", c.AdminService},
{"job_manager_service", c.JobManagerService},
{"jwt_service", c.JWTService},
}
for _, svc := range services {
if svc.service != nil {
status[svc.name] = "initialized"
} else {
status[svc.name] = "not_initialized"
}
}
// Job manager running status
if c.JobManagerService != nil {
if c.JobManagerService.IsRunning() {
status["job_manager_running"] = "true"
} else {
status["job_manager_running"] = "false"
}
}
return status
}