kra-new/AGENTS.md

6.8 KiB

Repository Guidelines

This is a Kratos service template. This file owns the layering contract agents must follow when changing the template.

Project structure

cmd/                      Entrypoint, Wire injector, main.go.
configs/                  Runtime config (config.yaml). No secrets.
internal/config/          Viper config models, loading, snapshots, and reloads.
internal/logging/         Structured logging sinks and source metadata.
internal/server/          Gin handlers, middleware, routers, static files, HTTP wiring.
internal/service/dto/     Hand-written request and response DTOs.
internal/service/         DTO/DO transport adapters; organized by domain.
internal/biz/             Domain models, usecases, repo interfaces, errors.
internal/data/            Repo implementations, database clients, migrations.
internal/initialize/      First-install and configuration orchestration.
internal/integration/     External I/O adapters: cache, email, payment, storage (mq, websocket, runtimeconfig, systeminfo).
internal/modules/         Built-in module schema, seed, menu, API, and task contributions.
internal/paymentkit/      Shared payment normalization, signing, and JSON helpers.
internal/routecatalog/    HTTP route metadata and runtime policies.
internal/worker/          Timed-task runtime and scheduler.
internal/utils/           Stateless, internal-only helper packages.
pkg/                      Reusable infrastructure packages.
web/                      Vue administration frontend.
docs/                     Review notes, migration plans, and operational documentation.

Layering & dependency rules

Three model shapes flow through three layers. biz owns the DO, data owns the PO; service is a pass-through that converts at its boundary.

   client ──► DTO ──► service ──► DO ──► biz ──► DO ──► data ──► PO ──► storage
                                  ▲                ▲
                                  │ declares       │ implements
                                  └─── repo IF ────┘

   DTO  Data Transfer Object — hand-written HTTP request / response.
   DO   Domain Object        — pure biz model, no proto, no storage tags.
   PO   Persistent Object    — storage shape, owned by `data`.
Layer Owns Speaks at boundary Never speaks
service DTO ↔ DO PO, storage client
biz DO DO DTO, PO, storage client
data PO DO ↔ PO DTO
  • service imports internal/service/dto (DTO) and biz (DO). Never data.
  • biz never imports service or data. The repo interface declared here is the inversion seam.
  • data imports biz to implement the repo interface. Never service, never DTOs.
  • integration implements external I/O boundaries and may import biz and provider SDKs. It is not a utility layer.
  • utils contains stateless helpers only. It must not own clients, watchers, repositories, runtime configuration, or provider SDK lifecycles.
  • cmd is the only place that wires all layers via Wire.

A change crossing these arrows the wrong way is a layering bug; fix the design rather than add the import.

Layer responsibilities

service (DTO ↔ DO)

  • Convert hand-written HTTP DTOs into DOs at the service boundary and build response DTOs from returned DOs.
  • Keep handlers focused on binding, authentication context, response envelopes, and transport-specific limits.
  • Validate request inputs at the service boundary before delegating to the usecase.
  • Return biz errors. No business rules, no storage access, no PO.

biz (DO only)

  • Owns the DO (type <Resource> struct — no proto, no storage tags), the usecase, and the repo interface (type <Resource>Repo interface).
  • Owns typed errors built with errors.NotFound / errors.BadRequest and stable reason strings.
  • Owns ListOption helpers — ListFilter, ListOrderBy, ListOffset, ListLimit — so callers compose queries without leaking storage primitives.

data (DO ↔ PO)

  • Repo shape: implement biz.<Resource>Repo. The constructor returns the interface, never the concrete type: func New<Resource>Repo(d *Data) biz.<Resource>Repo.
  • PO and conversion: define a PO when the storage shape diverges from the DO. PO types stay inside data. Use free functions new<Resource> (DO → PO, write) and toBiz (PO → DO, read). Driver-specific builder types never leave data.
  • Shared clients: *Data (declared in internal/data/data.go) holds long-lived storage clients. Repos receive *Data and never construct their own clients.
  • Querying: translate ListOptions.Filter and ListOptions.OrderBy into the storage driver's query language inside the repo.
  • Errors: map driver errors to biz typed errors so callers above never branch on the driver.

server

  • Construct the Gin engine and Kratos HTTP server, apply middleware, register handlers and routes. No persistence access or business rules.

Add-a-resource checklist

  1. DTO: define request and response types in internal/service/dto/.
  2. DO + repo interface: declare both in biz; build the usecase on top of the interface.
  3. Repo impl: implement in data returning biz.<Resource>Repo; add a PO and the matching conversion helpers when storage shape diverges from DO.
  4. Transport: add the service adapter, handler, router registration, and routecatalog metadata.
  5. Wiring: register the repo constructor in data.ProviderSet, the usecase in biz.ProviderSet, and the service/handler providers in their sets.
  6. Regenerate: run make generate or make all to refresh Wire and go.mod.

Testing seam

Tests live beside the code they cover (*_test.go). Test layers in isolation: service tests fake the usecase, biz tests fake the repo, data tests exercise repo implementations at the storage boundary.

Generation & generated files

Regenerate via make generate or make all; never hand-edit wire_gen.go.

Naming & error reasons

  • Resource: <Resource> (e.g., Todo); collection RPC: List<Resources>.
  • Types: repo <Resource>Repo, usecase <Resource>Usecase, service <Resource>Service. PO types live inside internal/data/; pick a name that fits the storage driver and convert with new<Resource>(do) / toBiz(po) free functions.
  • Error reasons use stable strings and are surfaced as Err<Resource><Cause> in biz.

Commits & security

  • Conventional Commits: feat:, fix:, refactor:, chore(deps):, docs:, test:. Regenerated files belong in the same commit as their source.
  • Never commit real credentials in configs/config.yaml.