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, repos, cfg) validators := a.initValidators() middleware := a.initMiddleware(services, cfg) 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.reportService, services.tableService, validators.tableValidator, services.unitService, services.ingredientService, services.productRecipeService, services.vendorService, validators.vendorValidator, services.purchaseOrderService, validators.purchaseOrderValidator, services.unitConverterService, validators.unitConverterValidator, services.chartOfAccountTypeService, validators.chartOfAccountTypeValidator, services.chartOfAccountService, validators.chartOfAccountValidator, services.accountService, validators.accountValidator, *services.orderIngredientTransactionService, validators.orderIngredientTransactionValidator, services.gamificationService, validators.gamificationValidator, services.rewardService, validators.rewardValidator, services.campaignService, validators.campaignValidator, services.customerAuthService, validators.customerAuthValidator, services.customerPointsService, middleware.customerAuthMiddleware, ) 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 paymentOrderItemRepo *repository.PaymentOrderItemRepositoryImpl paymentMethodRepo *repository.PaymentMethodRepositoryImpl fileRepo *repository.FileRepositoryImpl customerRepo *repository.CustomerRepository analyticsRepo *repository.AnalyticsRepositoryImpl tableRepo *repository.TableRepository unitRepo *repository.UnitRepository ingredientRepo *repository.IngredientRepository productRecipeRepo *repository.ProductRecipeRepository vendorRepo *repository.VendorRepositoryImpl purchaseOrderRepo *repository.PurchaseOrderRepositoryImpl unitConverterRepo *repository.IngredientUnitConverterRepositoryImpl chartOfAccountTypeRepo *repository.ChartOfAccountTypeRepositoryImpl chartOfAccountRepo *repository.ChartOfAccountRepositoryImpl accountRepo *repository.AccountRepositoryImpl orderIngredientTransactionRepo *repository.OrderIngredientTransactionRepositoryImpl customerPointsRepo *repository.CustomerPointsRepository customerTokensRepo *repository.CustomerTokensRepository tierRepo *repository.TierRepository gameRepo *repository.GameRepository gamePrizeRepo *repository.GamePrizeRepository gamePlayRepo *repository.GamePlayRepository omsetTrackerRepo *repository.OmsetTrackerRepository rewardRepo repository.RewardRepository campaignRepo repository.CampaignRepository customerAuthRepo repository.CustomerAuthRepository customerPointsRepo repository.CustomerPointsRepository otpRepo repository.OtpRepository txManager *repository.TxManager } 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), paymentOrderItemRepo: repository.NewPaymentOrderItemRepositoryImpl(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), productRecipeRepo: repository.NewProductRecipeRepository(a.db), vendorRepo: repository.NewVendorRepositoryImpl(a.db), purchaseOrderRepo: repository.NewPurchaseOrderRepositoryImpl(a.db), unitConverterRepo: repository.NewIngredientUnitConverterRepositoryImpl(a.db).(*repository.IngredientUnitConverterRepositoryImpl), chartOfAccountTypeRepo: repository.NewChartOfAccountTypeRepositoryImpl(a.db), chartOfAccountRepo: repository.NewChartOfAccountRepositoryImpl(a.db), accountRepo: repository.NewAccountRepositoryImpl(a.db), orderIngredientTransactionRepo: repository.NewOrderIngredientTransactionRepositoryImpl(a.db).(*repository.OrderIngredientTransactionRepositoryImpl), customerPointsRepo: repository.NewCustomerPointsRepository(a.db), customerTokensRepo: repository.NewCustomerTokensRepository(a.db), tierRepo: repository.NewTierRepository(a.db), gameRepo: repository.NewGameRepository(a.db), gamePrizeRepo: repository.NewGamePrizeRepository(a.db), gamePlayRepo: repository.NewGamePlayRepository(a.db), omsetTrackerRepo: repository.NewOmsetTrackerRepository(a.db), rewardRepo: repository.NewRewardRepository(a.db), campaignRepo: repository.NewCampaignRepository(a.db), customerAuthRepo: repository.NewCustomerAuthRepository(a.db), customerPointsRepo: repository.NewCustomerPointsRepository(a.db), otpRepo: repository.NewOtpRepository(a.db), txManager: repository.NewTxManager(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 productRecipeProcessor *processor.ProductRecipeProcessorImpl vendorProcessor *processor.VendorProcessorImpl purchaseOrderProcessor *processor.PurchaseOrderProcessorImpl unitConverterProcessor *processor.IngredientUnitConverterProcessorImpl chartOfAccountTypeProcessor *processor.ChartOfAccountTypeProcessorImpl chartOfAccountProcessor *processor.ChartOfAccountProcessorImpl accountProcessor *processor.AccountProcessorImpl orderIngredientTransactionProcessor *processor.OrderIngredientTransactionProcessorImpl customerPointsProcessor *processor.CustomerPointsProcessor customerTokensProcessor *processor.CustomerTokensProcessor tierProcessor *processor.TierProcessor gameProcessor *processor.GameProcessor gamePrizeProcessor *processor.GamePrizeProcessor gamePlayProcessor *processor.GamePlayProcessor omsetTrackerProcessor *processor.OmsetTrackerProcessor rewardProcessor processor.RewardProcessor campaignProcessor processor.CampaignProcessor customerAuthProcessor processor.CustomerAuthProcessor customerPointsProcessor *processor.CustomerPointsProcessor otpProcessor processor.OtpProcessor fileClient processor.FileClient inventoryMovementService service.InventoryMovementService } func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processors { fileClient := client.NewFileClient(cfg.S3Config) fonnteClient := client.NewFonnteClient(cfg.GetFonnte()) otpProcessor := processor.NewOtpProcessor(fonnteClient, repos.otpRepo) inventoryMovementService := service.NewInventoryMovementService(repos.inventoryMovementRepo, repos.ingredientRepo) 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, repos.ingredientRepo, repos.inventoryMovementRepo), orderProcessor: processor.NewOrderProcessorImpl(repos.orderRepo, repos.orderItemRepo, repos.paymentRepo, repos.paymentOrderItemRepo, repos.productRepo, repos.paymentMethodRepo, repos.inventoryRepo, repos.inventoryMovementRepo, repos.productVariantRepo, repos.outletRepo, repos.customerRepo, repos.txManager, repos.productRecipeRepo, repos.ingredientRepo, inventoryMovementService), 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), productRecipeProcessor: processor.NewProductRecipeProcessor(repos.productRecipeRepo, repos.productRepo, repos.ingredientRepo), vendorProcessor: processor.NewVendorProcessorImpl(repos.vendorRepo), purchaseOrderProcessor: processor.NewPurchaseOrderProcessorImpl(repos.purchaseOrderRepo, repos.vendorRepo, repos.ingredientRepo, repos.unitRepo, repos.fileRepo, inventoryMovementService, repos.unitConverterRepo), unitConverterProcessor: processor.NewIngredientUnitConverterProcessorImpl(repos.unitConverterRepo, repos.ingredientRepo, repos.unitRepo), chartOfAccountTypeProcessor: processor.NewChartOfAccountTypeProcessorImpl(repos.chartOfAccountTypeRepo), chartOfAccountProcessor: processor.NewChartOfAccountProcessorImpl(repos.chartOfAccountRepo, repos.chartOfAccountTypeRepo), accountProcessor: processor.NewAccountProcessorImpl(repos.accountRepo, repos.chartOfAccountRepo), orderIngredientTransactionProcessor: processor.NewOrderIngredientTransactionProcessorImpl(repos.orderIngredientTransactionRepo, repos.productRecipeRepo, repos.ingredientRepo, repos.unitRepo).(*processor.OrderIngredientTransactionProcessorImpl), customerPointsProcessor: processor.NewCustomerPointsProcessor(repos.customerPointsRepo), customerTokensProcessor: processor.NewCustomerTokensProcessor(repos.customerTokensRepo), tierProcessor: processor.NewTierProcessor(repos.tierRepo), gameProcessor: processor.NewGameProcessor(repos.gameRepo), gamePrizeProcessor: processor.NewGamePrizeProcessor(repos.gamePrizeRepo), gamePlayProcessor: processor.NewGamePlayProcessor(repos.gamePlayRepo, repos.gameRepo, repos.gamePrizeRepo, repos.customerTokensRepo, repos.customerPointsRepo), omsetTrackerProcessor: processor.NewOmsetTrackerProcessor(repos.omsetTrackerRepo), rewardProcessor: processor.NewRewardProcessor(repos.rewardRepo), campaignProcessor: processor.NewCampaignProcessor(repos.campaignRepo), customerAuthProcessor: processor.NewCustomerAuthProcessor(repos.customerAuthRepo, otpProcessor, repos.otpRepo, cfg.GetCustomerJWTSecret(), cfg.GetCustomerJWTExpiresTTL()), customerPointsProcessor: processor.NewCustomerPointsProcessor(repos.customerPointsRepo), otpProcessor: otpProcessor, fileClient: fileClient, inventoryMovementService: inventoryMovementService, } } 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 reportService service.ReportService tableService *service.TableServiceImpl unitService *service.UnitServiceImpl ingredientService *service.IngredientServiceImpl productRecipeService *service.ProductRecipeServiceImpl vendorService *service.VendorServiceImpl purchaseOrderService *service.PurchaseOrderServiceImpl unitConverterService *service.IngredientUnitConverterServiceImpl chartOfAccountTypeService service.ChartOfAccountTypeService chartOfAccountService service.ChartOfAccountService accountService service.AccountService orderIngredientTransactionService *service.OrderIngredientTransactionService gamificationService service.GamificationService rewardService service.RewardService campaignService service.CampaignService customerAuthService service.CustomerAuthService customerPointsService service.CustomerPointsService } func (a *App) initServices(processors *processors, repos *repositories, 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, repos.tableRepo, nil, processors.orderIngredientTransactionProcessor, *repos.productRecipeRepo, repos.txManager) // Will be updated after orderIngredientTransactionService is created paymentMethodService := service.NewPaymentMethodService(processors.paymentMethodProcessor) fileService := service.NewFileServiceImpl(processors.fileProcessor) var customerService service.CustomerService = service.NewCustomerService(processors.customerProcessor) analyticsService := service.NewAnalyticsServiceImpl(processors.analyticsProcessor) reportService := service.NewReportService(analyticsService, repos.organizationRepo, repos.outletRepo, processors.fileClient) tableService := service.NewTableService(processors.tableProcessor, transformer.NewTableTransformer()) unitService := service.NewUnitService(processors.unitProcessor) ingredientService := service.NewIngredientService(processors.ingredientProcessor) productRecipeService := service.NewProductRecipeService(processors.productRecipeProcessor) vendorService := service.NewVendorService(processors.vendorProcessor) purchaseOrderService := service.NewPurchaseOrderService(processors.purchaseOrderProcessor) unitConverterService := service.NewIngredientUnitConverterService(processors.unitConverterProcessor) chartOfAccountTypeService := service.NewChartOfAccountTypeService(processors.chartOfAccountTypeProcessor) chartOfAccountService := service.NewChartOfAccountService(processors.chartOfAccountProcessor) accountService := service.NewAccountService(processors.accountProcessor) orderIngredientTransactionService := service.NewOrderIngredientTransactionService(processors.orderIngredientTransactionProcessor, repos.txManager) gamificationService := service.NewGamificationService(processors.customerPointsProcessor, processors.customerTokensProcessor, processors.tierProcessor, processors.gameProcessor, processors.gamePrizeProcessor, processors.gamePlayProcessor, processors.omsetTrackerProcessor) rewardService := service.NewRewardService(processors.rewardProcessor) campaignService := service.NewCampaignService(processors.campaignProcessor) customerAuthService := service.NewCustomerAuthService(processors.customerAuthProcessor) customerPointsService := service.NewCustomerPointsService(processors.customerPointsProcessor) // Update order service with order ingredient transaction service orderService = service.NewOrderServiceImpl(processors.orderProcessor, repos.tableRepo, orderIngredientTransactionService, processors.orderIngredientTransactionProcessor, *repos.productRecipeRepo, repos.txManager) 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, reportService: reportService, tableService: tableService, unitService: unitService, ingredientService: ingredientService, productRecipeService: productRecipeService, vendorService: vendorService, purchaseOrderService: purchaseOrderService, unitConverterService: unitConverterService, chartOfAccountTypeService: chartOfAccountTypeService, chartOfAccountService: chartOfAccountService, accountService: accountService, orderIngredientTransactionService: orderIngredientTransactionService, gamificationService: gamificationService, rewardService: rewardService, campaignService: campaignService, customerAuthService: customerAuthService, customerPointsService: customerPointsService, } } type middlewares struct { authMiddleware *middleware.AuthMiddleware customerAuthMiddleware *middleware.CustomerAuthMiddleware } func (a *App) initMiddleware(services *services, cfg *config.Config) *middlewares { return &middlewares{ authMiddleware: middleware.NewAuthMiddleware(services.authService), customerAuthMiddleware: middleware.NewCustomerAuthMiddleware(cfg.GetCustomerJWTSecret()), } } 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 vendorValidator *validator.VendorValidatorImpl purchaseOrderValidator *validator.PurchaseOrderValidatorImpl unitConverterValidator *validator.IngredientUnitConverterValidatorImpl chartOfAccountTypeValidator *validator.ChartOfAccountTypeValidatorImpl chartOfAccountValidator *validator.ChartOfAccountValidatorImpl accountValidator *validator.AccountValidatorImpl orderIngredientTransactionValidator *validator.OrderIngredientTransactionValidatorImpl gamificationValidator *validator.GamificationValidatorImpl rewardValidator validator.RewardValidator campaignValidator validator.CampaignValidator customerAuthValidator validator.CustomerAuthValidator } 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(), vendorValidator: validator.NewVendorValidator(), purchaseOrderValidator: validator.NewPurchaseOrderValidator(), unitConverterValidator: validator.NewIngredientUnitConverterValidator().(*validator.IngredientUnitConverterValidatorImpl), chartOfAccountTypeValidator: validator.NewChartOfAccountTypeValidator().(*validator.ChartOfAccountTypeValidatorImpl), chartOfAccountValidator: validator.NewChartOfAccountValidator().(*validator.ChartOfAccountValidatorImpl), accountValidator: validator.NewAccountValidator().(*validator.AccountValidatorImpl), orderIngredientTransactionValidator: validator.NewOrderIngredientTransactionValidator().(*validator.OrderIngredientTransactionValidatorImpl), gamificationValidator: validator.NewGamificationValidator(), rewardValidator: validator.NewRewardValidator(), campaignValidator: validator.NewCampaignValidator(), customerAuthValidator: validator.NewCustomerAuthValidator(), } }