6.5 KiB
6.5 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/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 |
serviceimportsinternal/service/dto(DTO) andbiz(DO). Neverdata.biznever importsserviceordata. The repo interface declared here is the inversion seam.dataimportsbizto implement the repo interface. Neverservice, never DTOs.integrationimplements external I/O boundaries and may importbizand provider SDKs. It is not a utility layer.utilscontains stateless helpers only. It must not own clients, watchers, repositories, runtime configuration, or provider SDK lifecycles.cmdis 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
bizerrors. 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.BadRequestand stable reason strings. - Owns
ListOptionhelpers —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 functionsnew<Resource>(DO → PO, write) andtoBiz(PO → DO, read). Driver-specific builder types never leavedata. - Shared clients:
*Data(declared ininternal/data/data.go) holds long-lived storage clients. Repos receive*Dataand never construct their own clients. - Querying: translate
ListOptions.FilterandListOptions.OrderByinto the storage driver's query language inside the repo. - Errors: map driver errors to
biztyped 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
- DTO: define request and response types in
internal/service/dto/. - DO + repo interface: declare both in
biz; build the usecase on top of the interface. - Repo impl: implement in
datareturningbiz.<Resource>Repo; add a PO and the matching conversion helpers when storage shape diverges from DO. - Transport: add the service adapter, handler, router registration, and routecatalog metadata.
- Wiring: register the repo constructor in
data.ProviderSet, the usecase inbiz.ProviderSet, and the service/handler providers in their sets. - Regenerate: run
make generateormake allto refresh Wire andgo.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 insideinternal/data/; pick a name that fits the storage driver and convert withnew<Resource>(do)/toBiz(po)free functions. - Error reasons use stable strings and are surfaced as
Err<Resource><Cause>inbiz.
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.