package main import ( "business-engine/internal/config" "business-engine/internal/database" "business-engine/internal/payroll" "context" "log/slog" "net/http" "os" "os/signal" "syscall" "time" "github.com/jackc/pgx/v5/pgxpool" ) // Server contains all dependencies of the application type Server struct { config *config.Config logger *slog.Logger db *pgxpool.Pool router *http.ServeMux } // NewServer constructs a new server func NewServer(cfg *config.Config, logger *slog.Logger) (*Server, error) { dbpool, err := pgxpool.New(context.Background(), cfg.DatabaseURL) if err != nil { logger.Error("failed to connect to database", "error", err) os.Exit(1) } server := &Server{ config: cfg, logger: logger, db: dbpool, router: http.NewServeMux(), } server.setupRoutes() return server, nil } // setupRoutes initializes all routes and handlers func (s *Server) setupRoutes() { employeeRepo := database.New(s.db) zuschlagParser := payroll.NewZuschlagParser(s.logger) employeeService := payroll.NewService(employeeRepo, s.logger, zuschlagParser) employeeHandler := payroll.NewEmployeeHandler(*employeeService, s.logger) s.router.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("OK")) }) s.router.HandleFunc("GET /api/v1/payroll", employeeHandler.RunPayroll) } func (s *Server) Start() { httpServer := &http.Server{ Addr: ":" + s.config.Port, Handler: s.router, } go func() { s.logger.Info("server starting", "port", s.config.Port) if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { s.logger.Error("server failed to start", "error", err) os.Exit(1) } }() stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt, syscall.SIGTERM) <-stop s.logger.Info("shutting down server gracefully") ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := httpServer.Shutdown(ctx); err != nil { s.logger.Error("server shutdown failed", "error", err) } else { s.logger.Info("server stopped") } }