Coding patterns¶
Implemented
Source:
doc/governance/Coding_Standards.md (353 lines)
The standard handler shape¶
func (h *Handler) CreateAllocation(w http.ResponseWriter, r *http.Request) {
// 1) correlation + claims at the TOP
ctx := r.Context()
corrID := middleware.CorrelationIDFrom(ctx)
claims, ok := middleware.ClaimsFrom(ctx)
if !ok {
h.writeErr(ctx, w, corrID, errors.NewToken("token_missing"))
return
}
// 2) decode + sanitize input
var in apigen.CreateAllocationRequest
if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
h.writeErr(ctx, w, corrID, errors.NewValidation("invalid_request", err))
return
}
// 3) map to domain type at the boundary
domain := mapping.AllocationCreateFromAPI(in)
domain.RequestedBy = claims.UserID
domain.OrgID = claims.OrgID
// 4) call service — NO HTTP types below this line
alloc, err := h.alloc.CreateRequested(ctx, domain)
if err != nil {
h.writeErr(ctx, w, corrID, err) // mapping in writeErr — sentinel → http code
return
}
// 5) sanitize output
out := mapping.AllocationToAPI(alloc)
middleware.Sanitize(&out)
// 6) respond
h.writeJSON(w, http.StatusCreated, out)
}
The standard service function shape¶
// Service function — no HTTP types here.
func (s *Service) CreateRequested(ctx context.Context, in CreateRequestedInput) (*Allocation, error) {
tx, err := s.db.Begin(ctx)
if err != nil {
return nil, err
}
defer tx.Rollback(ctx)
// 1) reserve placement (sentinel errors mapped at boundary)
nodeID, slotIDs, err := s.reservePlacement(ctx, tx, in)
if err != nil {
return nil, err // ErrSKUUnavailable, etc.
}
// 2) insert allocation
alloc, err := s.insertAllocation(ctx, tx, in, nodeID, slotIDs)
if err != nil {
return nil, err
}
// 3) outbox row IN SAME TX
if err := s.outbox.Insert(ctx, tx, outbox.Row{
EventType: "provisioning.requested",
Subject: "provisioning.requested",
CorrelationID: middleware.CorrelationIDFrom(ctx),
Envelope: envelope(alloc),
}); err != nil {
return nil, err
}
// 4) audit row IN SAME TX
if err := s.audit.Write(ctx, tx, audit.Row{
ActorUserID: in.RequestedBy,
ActorRole: "user",
Action: "allocation.create",
TargetType: "allocation",
TargetID: alloc.ID,
Result: "success",
CorrelationID: middleware.CorrelationIDFrom(ctx),
}); err != nil {
return nil, err
}
if err := tx.Commit(ctx); err != nil {
return nil, err
}
return alloc, nil
}
Mandatory rules¶
mindmap
root((Mandatory rules))
Contract-first
Edit OpenAPI/AsyncAPI before code
Domain fragments where migrated
Idempotent mutations
X-Idempotency-Key
Exception: terminal token mint
No hardcoded policy
policy.Client only
Test-only constants OK
Sanitize first
Middleware.Sanitize before log/trace
REDACTED never omitted
Immutable ledger
No UPDATE / DELETE on ledger
Corrections are new entries
No balance column
Outbox required
Domain change + outbox in 1 tx
Never NATS-publish from handler
Raw body for Stripe
Buffer before parse
Signature on exact bytes
No query-string tokens
Auth never in URL
WS uses Sec-WebSocket-Protocol
Audit required
Every privileged mutation
actor, role, action, target, result, corrID
DB access boundaries
Service queries only its tables
Cross-domain via events / API
CI portability
Logic in scripts/ci
YAML is wrapper only
Root-cause-first
No symptom-only fixes
Mark blocked if upstream
Postgres typing
::text ::int ::uuid casts
Test against real PG
5xx classification
Upstream vs local defect
Regression test required
Numbered rules from Coding_Standards.md¶
- Contract-first — every API/event change starts in
doc/api/. - Idempotent mutations — all POST/PUT/PATCH safe with same
X-Idempotency-Key. Exception: terminal token mint. - No hardcoded business policy constants — billing thresholds, rate limits, refund windows from
PolicyClient, not literals. - Sanitize first —
middleware.Sanitize()before any log/trace. - Immutable ledger — never UPDATE/DELETE
ledger_entries. Corrections are new entries. - Outbox for events — domain change + outbox in same tx. Never publish to NATS from a handler.
- Stripe raw-body-first — buffer raw body before JSON parse.
- No query-string tokens — auth material never in URL params.
- Audit required — every privileged mutation writes
audit_logs. - DB access boundaries — each service queries only its own tables.
- 10a. API-first ops verification — use admin/read-model APIs; direct SQL only temporarily.
- CI portability — gate logic in
scripts/ci/*.sh; YAML is wrapper. - Root-cause ownership — no symptom-only fixes. Mark
blockedif upstream. - Postgres polymorphic typing — explicit
::text/::int/::uuidcasts on bind params. - 5xx classification gate — every new 5xx path classified upstream vs local; add regression test.
Naming + structure¶
| Thing | Pattern |
|---|---|
| Service struct | type Service struct { db, log, policy, audit, outbox … } |
| Handler struct | type Handler struct { svc, ... } |
| Sentinel errors | var ErrSKUUnavailable = errors.New("sku unavailable") |
| API → domain mapping | packages/services/<svc>/mapping/ |
| Sanitize blocklist | maintained in packages/shared/middleware/sanitize.go |