Files
agentic-dev/CLAUDE.md
Danijel Martinek 892f924603 docs: surface turbo gen feature in AGENTS.md and CLAUDE.md
Wires the existing turbo gen feature generator into AGENTS.md (Adding
a Feature section, Key Commands, Specification & Guides) and
CLAUDE.md (Quick Start, Read First). Adds a fast-path callout at the
top of the manual walkthrough in docs/guides/adding-a-feature.md
pointing at scaffolding-a-feature.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 11:30:10 +02:00

8.0 KiB

Clean Architecture Monorepo Template

Quick Start

pnpm install           # Install all dependencies
pnpm dev               # Start all dev servers
pnpm build             # Build all packages
pnpm test              # Run all tests
pnpm turbo boundaries  # Validate workspace dependency graph
pnpm turbo gen feature # Scaffold a new feature package (see docs/guides/scaffolding-a-feature.md)
docker compose up -d   # Start PostgreSQL

TDD

pnpm test --watch --filter @repo/<feature>   # watch one feature
pnpm test -- --coverage                      # full run with coverage
pnpm test:stories                            # Storybook smoke tests
pnpm test:e2e                                # Playwright e2e

See docs/guides/tdd-workflow.md for the full cycle.

Project Overview

Turborepo + pnpm monorepo organized by vertical features. Each feature (auth, blog, media, marketing-pages, navigation) owns its Clean Architecture layers. Core packages (core-shared, core-cms, core-api, core-trpc, core-ui) provide foundation. Two tooling packages (core-eslint, core-typescript) provide shared configs. Workspace boundaries are enforced by ESLint (lint-time) and Turborepo (build-graph time). Supports Next.js and TanStack Start as frontend frameworks, Payload CMS for content management, and comprehensive agent-optimized documentation.

Read First

  • AGENTS.md — Package map, boundary rules, per-package conventions
  • docs/architecture/overview.md — High-level architecture and package responsibilities
  • docs/architecture/vertical-feature-spec.md — Design spec with rationale and decision log
  • docs/guides/scaffolding-a-feature.mdturbo gen feature reference (fast path; prefer this over the manual walkthrough)
  • docs/guides/adding-a-feature.md — End-to-end new feature walkthrough (manual path; for cases the generator's Phase-1 scope doesn't cover)

Key Conventions

  • Relative imports in src/ — Source files use relative paths (../repositories/...), not @/ alias
  • @/ alias in tests — Test files (*.test.ts) use @/ to import from src/
  • vitest.config.ts — Every package must define resolve.alias: { "@": path.resolve(__dirname, "./src") }
  • tsconfig.json rootDir — Set "rootDir": "." so TypeScript finds both src/ and test files
  • Lazar-conformant file layout — Entities live at entities/models/<x>.ts; errors at entities/errors/<domain>.ts + entities/errors/common.ts; mock siblings use the .mock.ts suffix (<x>.repository.mock.ts); real repository impls drop the payload- prefix (<x>.repository.ts); interface filenames are dot-separated (<x>.repository.interface.ts)
  • Factory-function use cases & controllers — Every use case and controller is (deps) => async (input) => result; each exports export type I*UseCase = ReturnType<typeof xUseCase> (and the analogous I*Controller); one controller per use case (no multi-method controllers)
  • DI uses .toDynamicValue() for factoriesbind<IXUseCase>(SYMBOL).toDynamicValue((ctx) => xUseCase(ctx.container.get(...))); mocks remain the default binding
  • Tests inject mocks directly — Construct MockXRepository and pass into the factory: signInUseCase(mockUsers, mockAuth)(input). No container rebinding in unit tests
  • Schemas in the use-case file — Every use case exports xInputSchema (a z.ZodObject with .strict(); z.object({}).strict() for void inputs) and, for non-void use cases, xOutputSchema. Types: XInput = z.infer<typeof xInputSchema> and XOutput. Use case body ends with xOutputSchema.parse(result) before returning (runtime guarantee against malformed repository data)
  • Controllers receive unknown + presenter — Controllers safeParse(xInputSchema) from the use-case file and throw InputParseError on failure. Non-void controllers define a top-level function presenter(value: XOutput) and return Promise<ReturnType<typeof presenter>> (identity is fine — return value); void controllers return Promise<void> with no presenter
  • Feature-scoped tRPC error mapping — Each feature has integrations/api/procedures.ts exporting xProcedure = t.procedure.use(defineErrorMiddleware([[Ctor, "TRPC_CODE"], ...])) from @repo/core-shared/trpc/define-error-middleware. Routers use xProcedure.input(xInputSchema) — schemas are imported from the use-case file, never redefined inline. core-shared never enumerates feature error classes
  • Public surface split — Feature root (.) exports contracts only: types, errors, schemas, IUseCase / IController aliases, router type, constants. UI artifacts (query builders, components) live behind ./ui (src/ui/index.ts). Apps import queries from @repo/<feature>/ui, schemas/types from @repo/<feature>
  • Payload repositories via constructor — Feature packages receive Payload config at constructor time, not as a direct dependency
  • Three binding modes per feature — Each feature exports two binders: ./di/bind-production (real Payload) and ./di/bind-dev-seed (populated mock). The app's bindAll() dispatcher in apps/web-next/src/server/bind-production.ts picks one by env: USE_DEV_SEED="true" → dev seed; NODE_ENV="production" → production; otherwise → dev seed (developer default so pnpm dev boots without Payload). Dev seed lives in src/__seeds__/dev.ts as a lazy buildDev<Entities>() function that uses the feature's existing factory
  • App bootstrap — Each app calls bindAll() from a server entry point (page server component, route handler) before resolving any feature controller. The dispatcher is idempotent
  • Instrumentation lives in core-shared/instrumentation/ — Two interfaces (ITracer, ILogger), three implementations (NoopTracer/NoopLogger, SentryTracer/SentryLogger, and RecordingTracer/RecordingLogger from core-testing). Feature packages MUST NOT import @sentry/* directly (R40, eslint-enforced)
  • Spans + capture composed at DI bind time — Use cases + controllers wrapped via withSpan(tracer, spanOpts, withCapture(logger, tags, factory(deps))) inside bind-production / bind-dev-seed. withSpan is outermost so an errored span's timing reflects the capture-and-rethrow. Repository methods are different — they call this.tracer.startSpan(...) and this.logger.captureException(...) inline per method because they own per-call attributes (R41, R42)
  • Capture at throw sites only, with double-report guard — Repos capture infra errors inline; use cases + controllers capture via withCapture at bind time; defineErrorMiddleware never captures (R43, R44). Each error gets a non-enumerable __sentryReported flag the first time it's captured; withCapture, SentryLogger, and RecordingLogger all bail if the flag is set, so a bubbled error surfaces exactly once with the inner-most layer's tags (helper at core-shared/instrumentation/reported-flag.ts)
  • PII handling is non-negotiablesendDefaultPii: false everywhere (R31, CI grep gate); replay default-masks all text/inputs/media (R34, R35, allowlist starts empty); Sentry.setUser({ id }) only — no email/username (R36); beforeSend + beforeSendTransaction scrubbers strip emails/passwords/tokens/cookies/auth/IPs (R32, R33)
  • Three apps, three Sentry projectsWEB_NEXT_SENTRY_DSN, CMS_SENTRY_DSN, WEB_TANSTACK_SENTRY_DSN. Browser DSNs use NEXT_PUBLIC_ (web-next) and VITE_ (web-tanstack) prefixes
  • Instrumentation binding is orthogonal to repo bindingbindAll()'s Rule 0 (DSN → Sentry vs Noop) is independent of USE_DEV_SEED / NODE_ENV. Run pnpm dev with WEB_NEXT_SENTRY_DSN set to test the integration locally

MCP Servers

Start Storybook before UI work: pnpm dev --filter @repo/storybook

Storybook MCP available at http://localhost:6006/mcp — use list-all-documentation to discover existing components before creating new ones.

Key Ports

Service Port
Next.js 3000
Payload CMS 3001
TanStack Start 3002
PostgreSQL 5432
Storybook 6006