Files
agentic-dev/docs/guides/scaffolding-a-feature.md

131 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Scaffolding a feature
`turbo gen feature` produces a Lazar-conformant feature package under
`packages/<name>/` matching the shape of the reference `navigation` feature.
## Invoking the generator
Interactive (recommended for first runs — Plop will prompt for each value):
```bash
pnpm turbo gen feature
```
Non-interactive (positional bypass — order matches the prompts in
`turbo/generators/config.ts`):
```bash
pnpm turbo gen feature --args <name> <Entity> <entities-plural>
```
| Position | Prompt | Example | Conventions |
|---|---|---|---|
| `<name>` | Feature package name | `widgets` | `kebab-case`, becomes `@repo/<name>` and `packages/<name>/` |
| `<Entity>` | Entity name | `Widget` | `PascalCase` singular, drives class/symbol/use-case names |
| `<entities-plural>` | Entity plural slug | `widgets` | `kebab-case`, used for the future Payload collection slug |
Example end-to-end:
```bash
pnpm turbo gen feature --args widgets Widget widgets
pnpm install # link the new workspace package
pnpm --filter @repo/widgets lint typecheck test
```
## Conformance-ready by default
Since milestone v of the conformance system, `pnpm turbo gen feature <name>` emits two conformance artefacts:
- **`src/feature.manifest.ts`** declaring the scaffolded `getX` use case
- **`src/di/bind-production.ts`** with `assertFeatureConformance(...)` called at the tail
Run `pnpm conformance` after generating a feature — it should pass cleanly. If you add `bus.publish("X")` calls in a factory body, you'll also need to add `"X"` to the manifest's `publishes[]` array for that use case, or the `no-undeclared-event-publish` ESLint rule will warn.
See `docs/guides/conformance-quickref.md` for the manifest field reference.
## What it generates
- Package files: `package.json`, `tsconfig.json`, `vitest.config.ts`,
`eslint.config.js`, `turbo.json`, `AGENTS.md`
- One entity (`src/entities/models/<entity>.ts`) with a Zod schema +
unit test
- One use case (`src/application/use-cases/get-<entity>.use-case.ts`) with
exported input/output schemas, factory function, and tests
- One controller (`src/interface-adapters/controllers/get-<entity>.controller.ts`)
with the canonical safeParse → presenter shape
- Mock + real repository (`src/infrastructure/repositories/<entity>.repository{,.mock}.ts`).
Both wrap calls in `tracer.startSpan`; the real repo also calls
`logger.captureException` on errors. The real repo body is a Phase-1
stub that returns `null` until you wire a Payload collection.
- DI: `symbols.ts`, `module.ts`, `container.ts`, plus
`bind-production.ts` and `bind-dev-seed.ts` that compose
`withSpan(tracer, opts, withCapture(logger, tags, factory(deps)))` at
bind time (post-R44 sandwich pattern)
- tRPC integration: `procedures.ts` (feature-scoped error middleware) and
`router.ts` exposing `get<Entity>` with full router tests including
`BAD_REQUEST` / `NOT_FOUND` mapping
- Contract suite (`__contracts__/`), dev seed (`__seeds__/dev.ts`), and
empty stubs for `__factories__/` and `ui/`
## Phase-1 scope (intentionally limited)
The generator does NOT yet emit:
- Payload CMS collection / global templates (`integrations/cms/**`)
- React Query option builders (`ui/query.ts`)
- Faker-driven `defineFactory<Entity>` factories (only stubs)
- Multi-entity / multi-use-case (one `get<Entity>` only)
- Aggregator wiring across the monorepo
Add these by hand once the entity stabilises. The generator's stub files
clearly mark each `TODO`.
## Manual aggregator wiring (printed on success)
After running the generator, hand-edit these files to mount the new
feature on the app's runtime composition graph:
1. **`apps/web-next/src/server/bind-production.ts`** — import
`bindProduction<Name>` and `bindDevSeed<Name>` and call them from the
`bindAll()` dispatcher (production branch + dev-seed branch).
2. **`packages/core-api/src/root.ts`** — import `<name>Router` from
`@repo/<name>/api` and mount it on the app router.
3. **`packages/core-api/package.json`** — add `"@repo/<name>": "workspace:*"`
to dependencies.
4. **`apps/web-next/package.json`** — add `"@repo/<name>": "workspace:*"`
to dependencies.
5. **(Later) Payload CMS** — add a collection at
`packages/<name>/src/integrations/cms/collections/<entities-plural>.ts`
and register it in `packages/core-cms/...`.
6. **Verify**: `pnpm --filter @repo/<name> lint typecheck test`
The same checklist is printed by the generator when it finishes.
## Adding events and jobs to a feature
Once a feature exists, augment it with cross-feature events or background jobs using the dedicated generators. See [`docs/guides/events-and-jobs.md`](./events-and-jobs.md) for full walkthroughs.
```bash
pnpm turbo gen event publish # publisher contract
pnpm turbo gen event consume # consumer handler + Payload event-task
pnpm turbo gen job # background job + TaskConfig
pnpm turbo gen realtime channel # realtime channel descriptor (ADR-016)
pnpm turbo gen realtime handler # inbound realtime handler (ADR-016)
```
The event/job generators insert at six fixed `// <gen:*>` anchor comments. Generated features include four of them automatically (the `// <gen:job-tasks>` location is in `integrations/cms/index.ts`, which is manually authored as part of Phase-2 wiring); pre-existing features were retrofitted in ADR-015.
The realtime generators insert at three additional fixed `// <gen:realtime-*>` anchor comments (`// <gen:realtime-channels>` in `src/index.ts`, `// <gen:realtime-handler-symbols>` in `src/di/symbols.ts`, `// <gen:realtime-handlers>` in both `bind-*.ts` files). Generated features include all three automatically; pre-existing features were retrofitted in ADR-016.
## Cross-links
- `CLAUDE.md` — Key Conventions (factory-style use cases, `.toDynamicValue()`,
schemas-in-use-case, three binding modes per feature, span + capture sandwich)
- `packages/navigation/AGENTS.md` — canonical reference shape the templates mirror
- `docs/architecture/vertical-feature-spec.md` — design rationale for the layout
- `docs/decisions/adr-012-lazar-conformance.md` — file naming + factory pattern
- `docs/decisions/adr-013-input-output-unification.md` — schemas-in-use-case + presenter
- `docs/decisions/adr-014-instrumentation-sentry.md` — span + capture wiring (R41R44)
- `docs/decisions/adr-015-events-and-jobs.md` — cross-feature events + background jobs
- `docs/decisions/adr-016-realtime-layer.md` — Socket.IO realtime channels + handlers