Skip to content

16 — Contributing With The Architecture

Migrated from root technical docs.

Use this document when you are about to add new code.

It is the shortest path from:

  • “I need to add a page, feature, worker flow, or domain rule”
  • to the correct repo location, boundary, and starter pattern.

This guide does not replace the canonical architecture docs. It compresses them into a practical contribution workflow.

Related canonical docs:

See the copyable examples in templates/README.md.


These are the contribution rules that matter most.

  1. Organize product code by feature first.
  2. Keep routes thin. Route files declare, validate, preload, and hand off.
  3. Keep transport, query, mutation, model, and page composition separate.
  4. Reusable business logic belongs in domain packages, not pages or routes.
  5. Cloudflare runtime specifics stay behind server, worker, and infra layers.
  6. Shared UI stays primitive and generic. Business-named UI stays feature-owned.
  7. One feature has one source of truth. Do not add a parallel implementation.
  8. Do not create new junk-drawer folders such as helpers, common, shared, or broad utils without a narrow owner.
  9. Use the canonical docs before inventing a new structure.
  10. Run architecture validation before pushing: pnpm ci:arch.

If a change needs an exception, document it explicitly in the owning doc instead of silently drifting from the standard.


These are the places contributors are most likely to reintroduce architecture drift.

WorkflowMain regression riskUse this pattern
New dashboard page or routethick routes, duplicated query logic, page logic in route filesthin route + feature page
New dashboard feature slicemixed ownership, lib/ dumping, unclear internal structurefeature template
New query or mutationinline keys, ad hoc invalidation, direct server importsquery or mutation template
New shared UI primitivefeature imports leaking into shared UIshared UI checklist
New domain rule or policybusiness logic trapped in UI/server wiringdomain module template
New worker capabilityCloudflare runtime leaking into feature logicworker feature template
New docs or plandocs sprawl, mixed canonical and temporary notesdocs placement guide

The repo already has CI checks for structural regressions. These templates make the correct structure the default before CI has to reject anything.


Run this checklist before creating a file.

  1. Is this app wiring, feature code, shared UI, domain logic, platform logic, or runtime adapter code?
  2. Is this feature-specific, or truly shared across multiple owners today?
  3. Does this belong in a route, a feature, a domain package, a worker feature, or an infra adapter?
  4. Is this browser-safe, or does it touch Cloudflare runtime, auth sessions, D1, KV, queues, or worker env bindings?
  5. Am I creating a second source of truth for an existing feature?
  6. Am I about to add a generic folder name because ownership is unclear?
  7. If I move this later, would the target owner already be obvious now?

If any answer is unclear, stop and check:


If you are adding…Put it here
Dashboard route declarationapps/dashboard/src/routes/{-$locale}/...
Dashboard page entryapps/dashboard/src/features/{feature}/pages/
Feature UIapps/dashboard/src/features/{feature}/components/ or packages/feature-*/src/components/
Feature query keys or query optionsowning feature query-keys.ts and queries.ts
Feature mutation hooksowning feature mutations.ts
Feature model logicowning feature model/
Shared low-level UI primitiveapps/dashboard/src/shared/ui/
Shared business rulepackages/domain-*/src/
Cloudflare worker application logicworkers/*/src/features/
Cloudflare adapters or binding accessworkers/*/src/infra/ or apps/dashboard/src/server/
Canonical architecture or placement docdocs/architecture/
ADRdocs/adr/
Runbookdocs/runbooks/
Active plandocs/plans/

Do not add new production code to deprecated workers/my-api/.


  1. Create or extend the owning feature under src/features/ or the owning packages/feature-* package.
  2. Keep the route file thin.
  3. Put page composition in pages/.
  4. Put server state in queries.ts and mutations.ts.
  5. Put mapping, filters, and validation helpers in model/.

Start from:

Add a query and mutation to an existing feature

Section titled “Add a query and mutation to an existing feature”
  1. Add or extend query-keys.ts.
  2. Add queryOptions(...) factories in queries.ts.
  3. Keep raw write calls in api.ts.
  4. Wrap writes in useMutation with explicit invalidation in mutations.ts.

Start from:

  1. Confirm it is generic and low-level.
  2. Confirm it does not import feature code.
  3. Use generic naming such as Button, Card, Dialog, Tabs.
  4. If the component is business-named, keep it in the owning feature.

Start from:

  1. Keep it framework-light and runtime-light.
  2. Export pure functions, validators, policies, or interfaces.
  3. Do not import React, routes, server function wrappers, or Cloudflare env.

Start from:

  1. Put orchestration in features/{domain}/.
  2. Keep runtime binding access in infra/.
  3. Keep the worker entrypoint thin.
  4. Use shared/ only for narrow worker-safe helpers and types.

Start from:

  1. Decide whether the doc is canonical, operational, temporary, or local-only.
  2. Put it in the correct docs section immediately.
  3. Link local-only docs from docs/README.md if they need discoverability.

Start from:


  • Do not put business logic in route files.
  • Do not put feature logic in src/shared/ or src/lib/.
  • Do not put worker logic in dashboard code.
  • Do not put Cloudflare env access in browser-safe layers.
  • Do not add helpers.ts, common.ts, shared.ts, or broad utils.ts files when a narrower owner exists.
  • Do not add a second implementation of an existing feature in a different package or app folder.
  • Do not promote a feature component into shared UI just because two call sites happen to use it.
  • Do not statically import server function modules from client-safe code.

For common addition paths, use the scaffold script:

Terminal window
pnpm scaffold dashboard-feature my-feature
pnpm scaffold dashboard-feature my-feature --route my-feature
pnpm scaffold domain-module my-domain
pnpm scaffold worker-feature worker-ingestion-process linking

Expected output paths:

  • dashboard-featureapps/dashboard/src/features/{feature}/... plus optional route files in apps/dashboard/src/routes/{-$locale}/(dashboard)/...
  • domain-modulepackages/domain-{name}/src/...
  • worker-feature worker-ingestion-process {feature}workers/ingestion-process/src/features/{feature}/...
  • shared ingestion runtime modules → packages/platform-ingestion/src/features/{feature}/...

Use --dry-run first if you want to inspect the output without writing files.

The script creates small starter files only. It does not try to guess complex business logic or own generated artifacts.


Run the smallest relevant checks, then run architecture validation.

Terminal window
pnpm ci:arch
pnpm lint
pnpm type-check

Also run any feature-level or worker-specific validation needed for your slice. See 15-architecture-checks.md for what CI will enforce.


These are real and documented. Do not copy them into new code unless the same exception is explicitly accepted.

  1. workers/consumer-api/src/index.ts is still a documented monolith. New worker work should follow the worker structure guide instead of copying that layout.
  2. workers/rate-limiter remains a single-file Durable Object because the worker is intentionally small.
  3. Some dashboard data layers live in packages/feature-* while page composition remains app-local. Follow the feature source-of-truth doc instead of assuming every dashboard feature is app-local.
  4. Root plans/ is deprecated. New plans go in docs/plans/.

When you encounter another exception, document why it differs and whether it is legacy-only or acceptable for new code.