Hallo Stroke

Backend Architecture

Layered architecture, dependency injection, and the scheduler.

Layered pattern

Every module is four files with one responsibility each:

Loading diagram...

Composition root

cmd/main.go is the composition root. It:

  1. Loads env via godotenv, inits the rotating logger (lumberjack), creates the Fiber app with a custom error handler.
  2. Registers health checks, CORS, static file routes, request logger.
  3. Calls database.ConnectDB() (GORM), creates Postgres ENUMs, runs AutoMigrate for all models.
  4. Builds services in initServices() with cross-module wiring:
    • forumRepo → forumService → forumAdapter
    • whatsappRepo → whatsappService(forumAdapter, repo)
    • pengingatRepo → pengingatService(repo, whatsappService)
  5. The WhatsApp service is stored in a package global (routes.globalWhatsAppService) so routes.Routing(app) can reuse it.
  6. routes.Routing(app) constructs the remaining repos/services/handlers per module and registers all routes.
  7. 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():

Loading diagram...

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.

On this page