# Architecture — Go Backend (modular monolith) Target: repository root. API-first, serving ERP Web, POS Web, E-Commerce/B2B, Android, iOS. ``` ┌────────────────────┐ │ Vue / Nuxt Frontend │ └─────────┬──────────┘ │ REST (JSON) ▼ ┌────────────────────┐ │ Go API │ chi router, modular monolith │ auth users master │ │ inventory sales │ │ purchasing finance│ │ hr salestracking │ │ reports system │ └───────┬─────┬──────┘ │ │ ┌──────┘ └──────┐ ▼ ▼ PostgreSQL Redis (source of truth) (cache/rate-limit/idempotency/queues) ``` ## 1. Principles - **Modular monolith** — no microservices. One binary, many modules. - **Business logic lives in service/domain layers**, never in HTTP handlers, never in SQL repositories. - **Layered per module:** HTTP handler → service (domain/business rules) → repository (pgx) → PostgreSQL. - **Transactions:** critical workflows run in explicit `pgx.Tx` (order → stock → payment → financial record). Commit/rollback follows Laravel's actual sequence (business-rules.md). - **PostgreSQL is the source of truth.** Redis is only cache/rate-limit/idempotency/queue-support. - **Money:** `shopspring/decimal` + `NUMERIC(15,2)`. Never `float32/float64`. - **Preserve Laravel behavior first** (business-rules.md); deviations are explicit and documented in migration-risks.md. ## 2. Repo layout (this repository root) ## 2. Repo layout (this repository root) ``` cmd/server/main.go # wiring: config, db, redis, router, middleware, modules internal/ # Go module (repository root) auth/ # login/logout/me, tokens (opaque), token revocation users/ # user CRUD, roles, permissions (admin/system) products/ # products + categories + units (master data) suppliers/ customers/ warehouses/ inventory/ # stocks, movements, opname, transfer sales/ # sales orders + POS purchasing/ # purchase orders + goods receipt + debt invoices/ # fakturisasi: invoices + receivables payments/ # payment + cashflow entries + vouchers reports/ # dashboard KPIs + exports (CSV) hr/ # employees, attendance, leaves, payroll, performance salestracking/ # visits, activities, routes, locations system/ # activity logs, settings activitylog/ # shared audit-log writer (module-agnostic) pkg/ database/ # pgx pool + tx helpers + sequence helper redis/ # client wrapper (cache, rate limit, idempotency) logger/ # structured slog with request ID httpresponse/ # envelope builders (data/meta, error) middleware/ # requestid, logging, cors, recovery, auth, rbac, ratelimit config/ # env-driven config decimal/ # money helpers on shopspring/decimal storage/ # file storage interface (local now, S3/R2 later) seq/ # atomic daily document-number sequences migrations/ # Atlas versioned migrations + checksum tests/ # unit + integration + parity tests ``` **Only modules confirmed by the Laravel app are created** — the list above matches domain-map.md exactly. No empty "architectural completeness" modules. ## 4. Module internals (per domain) Each business module is a package with: - `model.go` — structs mirroring PG schema (numeric fields as `decimal.Decimal` or pgtype) - `repository.go` — data access (pgx), no business rules - `service.go` — business logic, transactions, document numbering - `handler.go` — HTTP binding, request parsing, response envelopes - `validation.go` — request validation (mirrors Laravel FormRequests) HTTP → Service → Repository: ``` POST /api/v1/pos → pos.handler → sales.service.ProcessPOS(tx) → inventory.service.adjustStock(tx, ...) → invoices.service.CreateFromSO(tx, ...) → tx.Commit() ``` ## 5. Transactions (explicit, following Laravel) Laravel wraps these in `DB::transaction(...)` — Go replicates the same boundaries: | Workflow | Transaction steps | |---|---| | POS (`processPos`) | create SO → items → adjustStock(-qty) per line → create invoice → update SO status | | SO create (`storeOrder`) | create SO → items → reserve stock per line | | SO update (draft) | release reserved → delete items → recreate → re-reserve → recompute | | SO cancel | release reserved → status cancelled | | PO create (`storePO`) | create PO → items → compute totals | | GR (`storeGR`) | create GR → items → stock += received (raw) → PO status | | Pay invoice (`payInvoice`) | update invoice → create Payment → create Cashflow | | Pay debt (`payDebt`) | update PO → voucher used → create Cashflow | | Opname finalize | apply differences → adjustStock each → status completed | Sequence allocation (document numbers) happens **inside the same transaction** via `seq.Next(ctx, tx, prefix, date)`. ## 6. Concurrency controls - Stock rows: `SELECT ... FOR UPDATE` before any quantity mutation (locks serialize concurrent POS/GR on the same product/warehouse). - `stocks` unique `(product_id, warehouse_id)` preserved. - Document numbers: per-day DB sequences (atomic `nextval`), see migration-plan.md §Numbering. - Unique constraints: `so_number`, `po_number`, `invoice_number`, `payment_number`, `cashflow.transaction_number`, `movement_number`, `gr_number`, `opname_number`, `transfer_number`, `voucher_number`, product/customer/supplier/warehouse codes, employees `employee_number`+`nik`, `attendances(employee_id,attendance_date)`, `payrolls(employee_id,period_month,period_year)`. ## 7. Redis usage (justified only) - Rate limiting on auth + high-frequency endpoints. - Idempotency dedup for `POST /payments`, `POST /invoices`, `POST /pos` (keyed by `Idempotency-Key`). - Optional token-blacklist cache (authoritative state still in `personal_access_tokens`). - **Never** stores orders/payments/inventory/financial truth. ## 8. Background jobs - None required (Laravel is synchronous). Reserved for future PDF/notifications via Redis+Asynq only if needed. ## 9. Storage abstraction - `pkg/storage` interface: `Put/Get/Delete`. Local adapter now; S3/R2 behind `STORAGE_*` env later. Product images (≤2MB), attendance/visit photos. ## 10. Logging & errors - Structured `slog`; request ID + user ID on every line; never passwords/tokens/secrets. - Error taxonomy (see api-design.md §Errors): validation / auth / forbidden / not_found / conflict / business / internal. Internal errors never leak details to clients. ## 11. Config (env) ``` APP_ENV, APP_PORT, DATABASE_URL, REDIS_URL, TOKEN_TTL_HOURS, ALLOW_NEGATIVE_STOCK (default true → Laravel parity), TIMEZONE=Asia/Jakarta (goods receipt movements are mandatory), CORS_ORIGINS, RATE_LIMIT_*, STORAGE_* (future), COMPANY_* (invoice header) ``` `.env.example` only; secrets never committed. ## 12. Health & readiness - `GET /health` — liveness. `GET /ready` — DB + Redis connectivity. ## 13. Deployment - `Dockerfile` provides the production backend image. PostgreSQL and Redis are external services configured through `DATABASE_URL` and `REDIS_URL`. - Also runnable directly: `go run ./cmd/server` (Go ≥1.22 required locally).