GS
Opens language menu

Architecture

Understanding the startup sequence and where responsibilities live saves time when you add endpoints, tables, or whole new modules.

High-level startup sequence

At a high level, cmd/main.go does the following:

  1. config.Init() — builds a config.Config value. Today the important field is Database (*gorm.DB), created using environment variables from .env.
  2. providers.NewApp(cfg) — constructs the App struct:
    • App.DB — the same *gorm.DB instance.
    • App.Webserver — a gin.Default() engine (middleware and routes attach here).
    • App.Injector — a samber/do injector with the database registered under a named key (pkg/constants) so constructors can resolve dependencies.
  3. CLI branch — if os.Args contains migration/seed/script flags, script.Commands(app) runs and may exit before the HTTP server starts.
  4. HTTP middleware — e.g. CORS applied to App.Webserver.
  5. RegisterModule chain — each feature module registers providers and routes:
    • user.RegisterModule(app) first (provides UserRepository and user HTTP).
    • auth.RegisterModule(app) second (depends on user repository binding for login/register flows).
  6. run(app) — binds and listens (Run on Gin).

This mirrors larger backends where a central App object carries shared infrastructure while modules stay decoupled.

The App struct (mental model)

Think of App as the composition root:

FieldRole
DBShared database handle for scripts and anything that bypasses DI.
WebserverGin engine — routes and global middleware.
InjectorDI container for module wiring (controllers still receive *do.Injector where constructors need DB by name).

Keeping both DB and Injector avoids forcing every line of legacy code through DI while still allowing incremental adoption.

Request path (happy path)

For an authenticated JSON request:

  1. Gin matches route + method; global middleware (CORS, recovery, etc.) runs.
  2. Route group may attach Authenticate(jwtService) — validates Authorization: Bearer, sets user_id on context.
  3. Controller (under internal/presentation/controllers) binds JSON to DTO structs, calls validation helpers, invokes service methods.
  4. Service (under internal/app/service) encodes business rules: hashing, token issuance, orchestrating repositories.
  5. Repository interface (under domain/repositories) describes persistence operations.
  6. PostgreSQL implementation (under internal/infrastructure/postgresql) executes GORM queries.

Controllers should stay thin — if you notice SQL or business rules creeping in, move them down a layer.

Why RegisterModule order matters

The auth module reuses the user repository interface to look up emails, persist users on register, etc. That repository is registered inside user.RegisterModule. If you reversed the order, auth’s provider wiring could run before UserRepository exists in the injector, causing runtime panics when resolving dependencies.

Rule of thumb: register foundational modules (users, billing accounts, tenants) before modules that depend on them.

internal/ and cross-module imports

Go enforces that internal packages are only visible to siblings under the same subtree. Practical consequences in this starter:

  • modules/user/dto and modules/user/domain/repositories live outside internal so modules/auth can import user contracts.
  • modules/auth/jwt lives outside internal so middlewares can import the JWT service without violating the rule.

If you introduce a new cross‑cutting concern (for example a pkg/observability tracer), keep it either in pkg/ or behind small interfaces in providers/ — not deep inside one module’s internal tree.

Error handling and responses

Controllers typically build JSON with helpers from pkg/utils (response wrappers). Keep status codes consistent with your API style guide — the starter demonstrates common patterns for success and validation errors.

Where to extend

NeedLikely touch points
New authenticated resourceNew module with RegisterModule, new migration, new routes under /api/....
New public field on UserEntity + migration + DTO + repository methods + validation.
Stricter JWTmodules/auth/jwt claims, expiry constants, middleware checks.

The next page, Project structure, maps these concepts to folders and files you will actually open in your editor.