Backend Architecture
Layered architecture, dependency injection, and the scheduler.
Layered pattern
Every module is four files with one responsibility each:
Composition root
cmd/main.go is the composition root. It:
- Loads env via godotenv, inits the rotating logger (lumberjack), creates the Fiber app with a custom error handler.
- Registers health checks, CORS, static file routes, request logger.
- Calls
database.ConnectDB()(GORM), creates Postgres ENUMs, runsAutoMigratefor all models. - Builds services in
initServices()with cross-module wiring:forumRepo → forumService → forumAdapterwhatsappRepo → whatsappService(forumAdapter, repo)pengingatRepo → pengingatService(repo, whatsappService)
- The WhatsApp service is stored in a package global (
routes.globalWhatsAppService) soroutes.Routing(app)can reuse it. routes.Routing(app)constructs the remaining repos/services/handlers per module and registers all routes.- Notification services are wired via setter injection:
forumService.SetNotificationService(whatsappService)artikelService.SetArticleNotificationService(whatsappService)pengingatService.SetPushNotificationService(pushNotificationService)
Cross-module contracts
internal/shared/interface.go defines the contracts (ForumService, NotificationService, PengingatNotificationService, ArticleNotificationService, YouTubeNotificationService) and all cross-module DTOs (PengingatDTO, ForumThreadDTO, etc.). NoOp* implementations exist for tests/default wiring.
Request flow
Request hits the Fiber router. CORS and a request logger run first.
Optional JWT middleware runs: OptionalJWT (public read), JWTProtected (auth required), or AdminProtected (admin role required).
The handler validates input (go-playground/validator), calls the service, which calls the repository (GORM) against PostgreSQL.
Notifications (WhatsApp / web push) are dispatched in goroutines so the HTTP response is not blocked.
Scheduler
startPengingatScheduler runs a background goroutine that ticks every 20 seconds and calls pengingatService.CheckAndSendPengingatBySchedule():
See Backend Reminders for the full schedule-matching logic.
Logging
utils/logger.go configures a lumberjack rotating writer — logs/app.log, 100 MB × 10 backups × 90 days, compressed. It is used by both the stdlib logger and GORM's query logger.
Dev-only image storage
utils/saveImage.go persists multipart uploads to build/uploads/<dest>/<uuid>.<ext> and serves them via Fiber's static handler. The README recommends S3/GCS for production.