package app import ( "apskel-pos-be/internal/client" "apskel-pos-be/internal/transformer" "context" "log" "net/http" "os" "os/signal" "syscall" "time" "apskel-pos-be/config" "apskel-pos-be/internal/handler" "apskel-pos-be/internal/middleware" "apskel-pos-be/internal/processor" "apskel-pos-be/internal/repository" "apskel-pos-be/internal/router" "apskel-pos-be/internal/service" "apskel-pos-be/internal/validator" "gorm.io/gorm" ) type App struct { server *http.Server db *gorm.DB router *router.Router shutdown chan os.Signal } func NewApp(db *gorm.DB) *App { return &App{ db: db, shutdown: make(chan os.Signal, 1), } } func (a *App) Initialize(cfg *config.Config) error { repos := a.initRepositories() processors := a.initProcessors(cfg, repos) services := a.initServices(processors, cfg) validators := a.initValidators() middleware := a.initMiddleware(services) healthHandler := handler.NewHealthHandler() a.router = router.NewRouter( cfg, healthHandler, services.authService, middleware.authMiddleware, services.userService, validators.userValidator, services.organizationService, validators.organizationValidator, services.outletService, validators.outletValidator, services.outletSettingService, services.categoryService, validators.categoryValidator, services.productService, validators.productValidator, services.productVariantService, validators.productVariantValidator, services.inventoryService, validators.inventoryValidator, services.orderService, validators.orderValidator, services.fileService, validators.fileValidator, services.customerService, validators.customerValidator, services.paymentMethodService, validators.paymentMethodValidator, services.analyticsService, services.tableService, validators.tableValidator, services.unitService, services.ingredientService, ) return nil } func (a *App) Start(port string) error { engine := a.router.Init() a.server = &http.Server{ Addr: ":" + port, Handler: engine, ReadTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second, IdleTimeout: 60 * time.Second, } signal.Notify(a.shutdown, os.Interrupt, syscall.SIGTERM) go func() { log.Printf("Server starting on port %s", port) if err := a.server.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("Failed to start server: %v", err) } }() <-a.shutdown log.Println("Shutting down server...") ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() if err := a.server.Shutdown(ctx); err != nil { log.Printf("Server forced to shutdown: %v", err) return err } log.Println("Server exited gracefully") return nil } func (a *App) Shutdown() { close(a.shutdown) } type repositories struct { userRepo *repository.UserRepositoryImpl organizationRepo *repository.OrganizationRepositoryImpl outletRepo *repository.OutletRepositoryImpl outletSettingRepo *repository.OutletSettingRepositoryImpl categoryRepo *repository.CategoryRepositoryImpl productRepo *repository.ProductRepositoryImpl productVariantRepo *repository.ProductVariantRepositoryImpl inventoryRepo *repository.InventoryRepositoryImpl inventoryMovementRepo *repository.InventoryMovementRepositoryImpl orderRepo *repository.OrderRepositoryImpl orderItemRepo *repository.OrderItemRepositoryImpl paymentRepo *repository.PaymentRepositoryImpl paymentMethodRepo *repository.PaymentMethodRepositoryImpl fileRepo *repository.FileRepositoryImpl customerRepo *repository.CustomerRepository analyticsRepo *repository.AnalyticsRepositoryImpl tableRepo *repository.TableRepository unitRepo *repository.UnitRepository ingredientRepo *repository.IngredientRepository } func (a *App) initRepositories() *repositories { return &repositories{ userRepo: repository.NewUserRepository(a.db), organizationRepo: repository.NewOrganizationRepositoryImpl(a.db), outletRepo: repository.NewOutletRepositoryImpl(a.db), outletSettingRepo: repository.NewOutletSettingRepositoryImpl(a.db), categoryRepo: repository.NewCategoryRepositoryImpl(a.db), productRepo: repository.NewProductRepositoryImpl(a.db), productVariantRepo: repository.NewProductVariantRepositoryImpl(a.db), inventoryRepo: repository.NewInventoryRepositoryImpl(a.db), inventoryMovementRepo: repository.NewInventoryMovementRepositoryImpl(a.db), orderRepo: repository.NewOrderRepositoryImpl(a.db), orderItemRepo: repository.NewOrderItemRepositoryImpl(a.db), paymentRepo: repository.NewPaymentRepositoryImpl(a.db), paymentMethodRepo: repository.NewPaymentMethodRepositoryImpl(a.db), fileRepo: repository.NewFileRepositoryImpl(a.db), customerRepo: repository.NewCustomerRepository(a.db), analyticsRepo: repository.NewAnalyticsRepositoryImpl(a.db), tableRepo: repository.NewTableRepository(a.db), unitRepo: repository.NewUnitRepository(a.db), ingredientRepo: repository.NewIngredientRepository(a.db), } } type processors struct { userProcessor *processor.UserProcessorImpl organizationProcessor processor.OrganizationProcessor outletProcessor processor.OutletProcessor outletSettingProcessor *processor.OutletSettingProcessorImpl categoryProcessor processor.CategoryProcessor productProcessor processor.ProductProcessor productVariantProcessor processor.ProductVariantProcessor inventoryProcessor processor.InventoryProcessor orderProcessor processor.OrderProcessor paymentMethodProcessor processor.PaymentMethodProcessor fileProcessor processor.FileProcessor customerProcessor *processor.CustomerProcessor analyticsProcessor *processor.AnalyticsProcessorImpl tableProcessor *processor.TableProcessor unitProcessor *processor.UnitProcessorImpl ingredientProcessor *processor.IngredientProcessorImpl } func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processors { fileClient := client.NewFileClient(cfg.S3Config) return &processors{ userProcessor: processor.NewUserProcessor(repos.userRepo, repos.organizationRepo, repos.outletRepo), organizationProcessor: processor.NewOrganizationProcessorImpl(repos.organizationRepo, repos.outletRepo, repos.userRepo), outletProcessor: processor.NewOutletProcessorImpl(repos.outletRepo), outletSettingProcessor: processor.NewOutletSettingProcessorImpl(repos.outletSettingRepo, repos.outletRepo), categoryProcessor: processor.NewCategoryProcessorImpl(repos.categoryRepo), productProcessor: processor.NewProductProcessorImpl(repos.productRepo, repos.categoryRepo, repos.productVariantRepo, repos.inventoryRepo, repos.outletRepo), productVariantProcessor: processor.NewProductVariantProcessorImpl(repos.productVariantRepo, repos.productRepo), inventoryProcessor: processor.NewInventoryProcessorImpl(repos.inventoryRepo, repos.productRepo, repos.outletRepo), orderProcessor: processor.NewOrderProcessorImpl(repos.orderRepo, repos.orderItemRepo, repos.paymentRepo, repos.productRepo, repos.paymentMethodRepo, repos.inventoryRepo, repos.inventoryMovementRepo, repos.productVariantRepo, repos.outletRepo, repos.customerRepo), paymentMethodProcessor: processor.NewPaymentMethodProcessorImpl(repos.paymentMethodRepo), fileProcessor: processor.NewFileProcessorImpl(repos.fileRepo, fileClient), customerProcessor: processor.NewCustomerProcessor(repos.customerRepo), analyticsProcessor: processor.NewAnalyticsProcessorImpl(repos.analyticsRepo), tableProcessor: processor.NewTableProcessor(repos.tableRepo, repos.orderRepo), unitProcessor: processor.NewUnitProcessor(repos.unitRepo), ingredientProcessor: processor.NewIngredientProcessor(repos.ingredientRepo, repos.unitRepo), } } type services struct { userService *service.UserServiceImpl authService service.AuthService organizationService service.OrganizationService outletService service.OutletService outletSettingService service.OutletSettingService categoryService service.CategoryService productService service.ProductService productVariantService service.ProductVariantService inventoryService service.InventoryService orderService service.OrderService paymentMethodService service.PaymentMethodService fileService service.FileService customerService service.CustomerService analyticsService *service.AnalyticsServiceImpl tableService *service.TableServiceImpl unitService *service.UnitServiceImpl ingredientService *service.IngredientServiceImpl } func (a *App) initServices(processors *processors, cfg *config.Config) *services { authConfig := cfg.Auth() jwtSecret := authConfig.AccessTokenSecret() authService := service.NewAuthService(processors.userProcessor, jwtSecret) organizationService := service.NewOrganizationService(processors.organizationProcessor) outletService := service.NewOutletService(processors.outletProcessor) outletSettingService := service.NewOutletSettingService(processors.outletSettingProcessor) categoryService := service.NewCategoryService(processors.categoryProcessor) productService := service.NewProductService(processors.productProcessor) productVariantService := service.NewProductVariantService(processors.productVariantProcessor) inventoryService := service.NewInventoryService(processors.inventoryProcessor) orderService := service.NewOrderServiceImpl(processors.orderProcessor) paymentMethodService := service.NewPaymentMethodService(processors.paymentMethodProcessor) fileService := service.NewFileServiceImpl(processors.fileProcessor) var customerService service.CustomerService = service.NewCustomerService(processors.customerProcessor) analyticsService := service.NewAnalyticsServiceImpl(processors.analyticsProcessor) tableService := service.NewTableService(processors.tableProcessor, transformer.NewTableTransformer()) unitService := service.NewUnitService(processors.unitProcessor) ingredientService := service.NewIngredientService(processors.ingredientProcessor) return &services{ userService: service.NewUserService(processors.userProcessor), authService: authService, organizationService: organizationService, outletService: outletService, outletSettingService: outletSettingService, categoryService: categoryService, productService: productService, productVariantService: productVariantService, inventoryService: inventoryService, orderService: orderService, paymentMethodService: paymentMethodService, fileService: fileService, customerService: customerService, analyticsService: analyticsService, tableService: tableService, unitService: unitService, ingredientService: ingredientService, } } type middlewares struct { authMiddleware *middleware.AuthMiddleware } func (a *App) initMiddleware(services *services) *middlewares { return &middlewares{ authMiddleware: middleware.NewAuthMiddleware(services.authService), } } type validators struct { userValidator *validator.UserValidatorImpl organizationValidator validator.OrganizationValidator outletValidator validator.OutletValidator categoryValidator validator.CategoryValidator productValidator validator.ProductValidator productVariantValidator validator.ProductVariantValidator inventoryValidator validator.InventoryValidator orderValidator validator.OrderValidator paymentMethodValidator validator.PaymentMethodValidator fileValidator validator.FileValidator customerValidator validator.CustomerValidator tableValidator *validator.TableValidator } func (a *App) initValidators() *validators { return &validators{ userValidator: validator.NewUserValidator(), organizationValidator: validator.NewOrganizationValidator(), outletValidator: validator.NewOutletValidator(), categoryValidator: validator.NewCategoryValidator(), productValidator: validator.NewProductValidator(), productVariantValidator: validator.NewProductVariantValidator(), inventoryValidator: validator.NewInventoryValidator(), orderValidator: validator.NewOrderValidator(), paymentMethodValidator: validator.NewPaymentMethodValidator(), fileValidator: validator.NewFileValidatorImpl(), customerValidator: validator.NewCustomerValidator(), tableValidator: validator.NewTableValidator(), } }