Contents

This document helps you follow the project as a human without breaking the architecture. Deep architecture → DOMAIN.md. Writing a new module → YENI_MODUL_EKLEME.md.

Folder map in 5 minutes

cmd/server          → entry; bootstrap.Run
internal/bootstrap  → composition root (Build + wire_*.go)
internal/domain/X   → business rules, repository PORT (interface)
internal/application/X → use case (CQRS: command / query)
internal/adapters/http     → JSON API (Fiber handler)
internal/adapters/goui     → panel pages (Controller)
internal/adapters/persistence/postgres → sqlc repo (port implementation)
internal/infrastructure    → JWT, mail, SMS, payment, cache…
pkg/                → cross-cutting helpers (not domain)
db/queries/         → sqlc SQL source
migrations/         → schema
api/openapi.yaml    → API contract

Dependency direction (memorize):

domain ← application ← adapters · infrastructure on the outside · everything wired in bootstrap.

Context-specific file lists: map/user.md, map/auth.md, map/contact.md, map/notification.md, map/audit.md, map/payment.md, map/settings.md, map/upload.md, map/rbac.md.


Application Service surface (standard)

Transport (HTTP / GoUI) reads application/<context>.Service (or existing *Service) first. CQRS *Handler files stay internal; business rules live there.

ContextFacade fileGoUI / HTTP field
Userapplication/user/service.goUsers
Authapplication/auth/facade.goAuth
Contactapplication/contact/service.goContacts
Notificationapplication/notification/service.goNotifications
Auditapplication/audit/service.goAudit
Settingsapplication/settings/service.goSettings
Paymentapplication/payment (ThreeDSService)ThreeDSSvc
Uploadapplication/upload/service.goUpload
Authz / RBACapplication/authz/service.goAuthz

When adding a new context: write CQRS handlers → NewService facade → transport receives Service only.


Naming glossary (single language)

NameWherePurposeWhat it is not
Use case / *Handler (application)internal/application/...One business rule step (Login, ListUsers…)Does not know HTTP or HTML
HTTP handlerinternal/adapters/http/handlerParses JSON request → calls use caseDoes not write domain logic
GoUI Controllerinternal/adapters/gouiPage state: Mount / Render / HandleEventNot a REST handler
Repository portinternal/domain/...Persistence interfaceNo SQL / GORM
Repository impladapters/persistence/postgresFills port with sqlcDo not put business rules here
Depshandler / GoUI / use caseConstructor injection groupsNot a god-container
Service (facade)application/<ctx>/service.go or facade.goSingle surface transport reads; CQRS insideDoes not copy business rules; delegates
wire_*.gointernal/bootstrapComposition root piecesNo business logic

Type names in the application layer may still be *Handler (CQRS pattern). When reading, think of these as use cases; do not confuse with HTTP handlers.


How does a request flow? (general)

Request
  → Fiber middleware (auth, i18n, RBAC, idempotency…)
  → HTTP handler  OR  GoUI Controller
  → application.<Context>Service  (facade)
  → CQRS use case (command/query handler)
  → domain port (Repository, …)
  → postgres/sqlc adapter
  → response: JSON  |  HTML/WS diff

API and panel call the same Service / use case; business rules stay in one place.


Golden path A — User list (API)

GET /api/v1/users (permission: users:list)

Reading order:

  1. Route — internal/adapters/http/server.go (protected.Get("/", … List))
  2. Transport — internal/adapters/http/handler/user_handler.goList
  3. Use case — internal/application/user/service.go (Service facade) → query.go (ListHandler)
  4. Access — internal/application/user/access.go
  5. Port — internal/domain/user (Repository)
  6. Impl — internal/adapters/persistence/postgres/user_repository.go
  7. SQL — db/queries/ (user-related queries)
  8. Wiring — internal/bootstrap/wire_user.go + wire_http.go

Detailed file map: map/user.md.

Pagination: The panel uses page + limit (offset). The REST API on the same endpoint supports optional keyset pagination via the cursor query parameter; when next_cursor is present in the response, pass it for the next page. Offset fields are kept for backward compatibility.


Golden path B — Same list (panel)

GET /dashboard/users → screen users

Reading order:

  1. Route — internal/adapters/goui/routes.go (pageRoutes, path /dashboard/users)
  2. Factory — internal/adapters/goui/controllers.gocontrollerFor
  3. Page — internal/adapters/goui/controller_account_users.go (usersListController)
  4. Use case — same application/userServiceList (same as API)
  5. Wiring — internal/bootstrap/wire_goui.go (UserDeps)

Golden path C — Login (API + panel)

SurfaceEntry point
APIPOST /api/v1/auth/loginhandler/auth_handler.goapplication/authService.LoginLoginHandler
Panel/auth/logincontroller_public_auth.go → same Service.Login (web OAuth via authServiceWeb)

File map: map/auth.md. Deep dive: AUTH.md.


Single-process memory (no Redis)

Redis is not required for single-instance deployment. The following components use in-process memory:

ComponentLocationNotes
JWT refresh token storesecurity.MemoryTokenStoreSession rotation
Login guard / IP limitersecurity.MemoryLoginGuard, MemoryIPRateLimiterBrute-force
Authz Resolverapplication/authz/resolver.goRole-permission TTL cache
Settings Serviceapplication/settings/service.goPlatform settings cache

Horizontal scaling (multiple processes) requires shared store + invalidation — currently out of scope. Details: DEPLOY.md.


Smoke / golden-path tests

Lightweight checks (full DB bootstrap not required):

TestPackageWhat it verifies
Cursor encode/decodepkg/pagination/cursor_test.goKeyset cursor roundtrip
Keyset SQL generationadapters/persistence/postgres/user_keyset_test.gosqlc keyset queries compile
User list surfaceapplication/user/smoke_test.goListQuery.Cursor, Service.List

For full integration use fake ports via bootstrap/testkit; production wiring stays in wire_*.go files.

go test ./pkg/pagination/... ./internal/application/user/... ./internal/adapters/persistence/postgres/... -count=1

"Where do I look?" quick table

QuestionFirst file
Which code handles this URL? (API)adapters/http/server.go
Which screen is this URL? (panel)adapters/goui/routes.go
Where is the business rule?application/<context>/service.go (facade) → CQRS files
Entity / invariant?domain/<context>/
SQL?db/queries/ + persistence/postgres/
How are dependencies wired?bootstrap/wire_*.go
Permission constant?pkg/rbac
How to add a new context?YENI_MODUL_EKLEME.md

Writing checklist (short)

New feature or fix:

  1. Find the context (user, auth, payment…) — if missing, see YENI_MODUL_EKLEME.md
  2. Domain entity/port if needed
  3. Application use case (command or query)
  4. Persistence (sqlc + repo) if needed
  5. HTTP and/or GoUI transport
  6. RBAC permission if needed: pkg/rbac + route
  7. Relevant wire_*.go in bootstrap
  8. Update OpenAPI / i18n if needed
  9. go test relevant packages

Full bootstrap not required in tests — see bootstrap/testkit.