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.

Context Facade file GoUI / HTTP field
User application/user/service.go Users
Auth application/auth/facade.go Auth
Contact application/contact/service.go Contacts
Notification application/notification/service.go Notifications
Audit application/audit/service.go Audit
Settings application/settings/service.go Settings
Payment application/payment (ThreeDSService) ThreeDSSvc
Upload application/upload/service.go Upload
Authz / RBAC application/authz/service.go Authz

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


Naming glossary (single language)

Name Where Purpose What it is not
Use case / *Handler (application) internal/application/... One business rule step (Login, ListUsers…) Does not know HTTP or HTML
HTTP handler internal/adapters/http/handler Parses JSON request → calls use case Does not write domain logic
GoUI Controller internal/adapters/goui Page state: Mount / Render / HandleEvent Not a REST handler
Repository port internal/domain/... Persistence interface No SQL / GORM
Repository impl adapters/persistence/postgres Fills port with sqlc Do not put business rules here
Deps handler / GoUI / use case Constructor injection groups Not a god-container
Service (facade) application/<ctx>/service.go or facade.go Single surface transport reads; CQRS inside Does not copy business rules; delegates
wire_*.go internal/bootstrap Composition root pieces No 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/user ServiceList (same as API)
  5. Wiring — internal/bootstrap/wire_goui.go (UserDeps)

Golden path C — Login (API + panel)

Surface Entry point
API POST /api/v1/auth/loginhandler/auth_handler.goapplication/auth Service.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:

Component Location Notes
JWT refresh token store security.MemoryTokenStore Session rotation
Login guard / IP limiter security.MemoryLoginGuard, MemoryIPRateLimiter Brute-force
Authz Resolver application/authz/resolver.go Role-permission TTL cache
Settings Service application/settings/service.go Platform 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):

Test Package What it verifies
Cursor encode/decode pkg/pagination/cursor_test.go Keyset cursor roundtrip
Keyset SQL generation adapters/persistence/postgres/user_keyset_test.go sqlc keyset queries compile
User list surface application/user/smoke_test.go ListQuery.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

Question First 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.