# 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/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. internal/modules/ Built-in module schema, seed, menu, API, and task contributions. 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. ``` ## 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 struct` — no proto, no storage tags), the usecase, and the repo interface (`type 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.Repo`. The constructor returns the interface, never the concrete type: `func NewRepo(d *Data) biz.Repo`. - _PO and conversion_: define a PO when the storage shape diverges from the DO. PO types stay inside `data`. Use free functions `new` (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.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: `` (e.g., `Todo`); collection RPC: `List`. - Types: repo `Repo`, usecase `Usecase`, service `Service`. PO types live inside `internal/data/`; pick a name that fits the storage driver and convert with `new(do)` / `toBiz(po)` free functions. - Error reasons use stable strings and are surfaced as `Err` 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`.