Project conventions
These engineering rules and preferences guide changes to the application in this repository. Follow the development workflow for setup, tooling, generation, verification, and contribution procedures, and the documentation policy when updating application knowledge. Paths are relative to the repository root. Preferences and exceptions are stated explicitly.
Shared Go conventions are maintained in go42x's code-style guidance. The rules below cover application-specific choices and exceptions.
Code conventions
- Use
vfor validation tags, as configured in thevalidatorpackage. - Name
echo.Contextvariablesc. - Prefer the shared
tools.BufferSize*constants for buffer capacities when their values fit the requirement.
Architecture and data
cmd/appcomposes dependencies and owns process lifecycle. Feature services own business operations. Within each feature,domain/contains service inputs, domain errors, and shared concepts;models/contains persistence models;repository/owns persistence operations; versioned HTTP and gRPC adapters own transport conversion and registration.- Adapters validate pagination before invoking services. Use
tools.NormalizePaginationand the sharedtools.Pagination*constants for defaults and bounds across features. Align API contracts with the pagination rules. - Services and worker handlers own transaction boundaries. Call the shared
WithTransactionhelper there and propagate itstxCtxto all participating database operations. Repository data-access methods use the supplied context and must not manage transactions themselves. - Use
GetTx(ctx)for writes and reads that require primary consistency. UseGetReadDB(ctx)only when replica lag is acceptable. Persist required outbox events using the sametxCtxas the business change, so both commit or roll back together. Document operations where event recording is best effort. - Use UUIDs for entity references in public APIs and events intended for consumers outside this application. Keep numeric database IDs internal. Authorization must be enforced independently of identifier format.
- Keep migration files in
migrate/{engine}/. Name themYYYYMMDDHHMMSS_description.sqland use the same filename for the corresponding migration across database engines. - Repeated migration runs through Goose must preserve existing schema and application data. Require raw SQL replay safety
only when a documented recovery procedure depends on it. Include
UpandDownsections in the same file. Rollback must target only changes introduced by that migration; document any data loss or irreversible changes. - Add a new migration for changes to an already-applied schema. Editing an old migration does not update databases that have recorded it as applied. Test both a fresh database and an upgrade containing existing data.
- Use lowercase SQL keywords and
snake_casefor table and column names in migrations. - Store and compare timestamps in UTC. Normalize incoming timestamps with
UTC()at repository write boundaries, including future CLI writes.
Runtime and observability
- Global logging is allowed where needed.
- Use
snake_casefor structured log field names and metric label names. - Use fatal logging only for initialization failures in command entrypoints.
- Report terminal HTTP/gRPC server failures through a buffered
Errors()channel owned by each server. Close it when serving ends; normal shutdown must not emit an error. A terminal server failure must fail both readiness and liveness so Kubernetes can restart the container. Keep dependency availability checks in readiness. Run graceful shutdown when a termination signal is received.
Source: docs/handbook/conventions.md. Revision: 3841815ceb9e.