16 — Contributing With The Architecture
Section titled “16 — Contributing With The Architecture”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:
- 00-repo-map.md
- 01-code-placement.md
- 03-dependency-rules.md
- 05-worker-structure.md
- 07-feature-source-of-truth.md
- 08-feature-internals.md
- 09-tanstack-query-patterns.md
- 10-domain-packages.md
- 11-cloudflare-runtime-boundary.md
- 13-naming-conventions.md
- 15-architecture-checks.md
See the copyable examples in templates/README.md.
The minimum rule set
Section titled “The minimum rule set”These are the contribution rules that matter most.
- Organize product code by feature first.
- Keep routes thin. Route files declare, validate, preload, and hand off.
- Keep transport, query, mutation, model, and page composition separate.
- Reusable business logic belongs in domain packages, not pages or routes.
- Cloudflare runtime specifics stay behind server, worker, and infra layers.
- Shared UI stays primitive and generic. Business-named UI stays feature-owned.
- One feature has one source of truth. Do not add a parallel implementation.
- Do not create new junk-drawer folders such as
helpers,common,shared, or broadutilswithout a narrow owner. - Use the canonical docs before inventing a new structure.
- 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.
Contribution points that need guardrails
Section titled “Contribution points that need guardrails”These are the places contributors are most likely to reintroduce architecture drift.
| Workflow | Main regression risk | Use this pattern |
|---|---|---|
| New dashboard page or route | thick routes, duplicated query logic, page logic in route files | thin route + feature page |
| New dashboard feature slice | mixed ownership, lib/ dumping, unclear internal structure | feature template |
| New query or mutation | inline keys, ad hoc invalidation, direct server imports | query or mutation template |
| New shared UI primitive | feature imports leaking into shared UI | shared UI checklist |
| New domain rule or policy | business logic trapped in UI/server wiring | domain module template |
| New worker capability | Cloudflare runtime leaking into feature logic | worker feature template |
| New docs or plan | docs sprawl, mixed canonical and temporary notes | docs 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.
Code placement checklist
Section titled “Code placement checklist”Run this checklist before creating a file.
- Is this app wiring, feature code, shared UI, domain logic, platform logic, or runtime adapter code?
- Is this feature-specific, or truly shared across multiple owners today?
- Does this belong in a route, a feature, a domain package, a worker feature, or an infra adapter?
- Is this browser-safe, or does it touch Cloudflare runtime, auth sessions, D1, KV, queues, or worker env bindings?
- Am I creating a second source of truth for an existing feature?
- Am I about to add a generic folder name because ownership is unclear?
- If I move this later, would the target owner already be obvious now?
If any answer is unclear, stop and check:
Where new code should go
Section titled “Where new code should go”| If you are adding… | Put it here |
|---|---|
| Dashboard route declaration | apps/dashboard/src/routes/{-$locale}/... |
| Dashboard page entry | apps/dashboard/src/features/{feature}/pages/ |
| Feature UI | apps/dashboard/src/features/{feature}/components/ or packages/feature-*/src/components/ |
| Feature query keys or query options | owning feature query-keys.ts and queries.ts |
| Feature mutation hooks | owning feature mutations.ts |
| Feature model logic | owning feature model/ |
| Shared low-level UI primitive | apps/dashboard/src/shared/ui/ |
| Shared business rule | packages/domain-*/src/ |
| Cloudflare worker application logic | workers/*/src/features/ |
| Cloudflare adapters or binding access | workers/*/src/infra/ or apps/dashboard/src/server/ |
| Canonical architecture or placement doc | docs/architecture/ |
| ADR | docs/adr/ |
| Runbook | docs/runbooks/ |
| Active plan | docs/plans/ |
Do not add new production code to deprecated workers/my-api/.
Common contribution workflows
Section titled “Common contribution workflows”Add a new dashboard feature page
Section titled “Add a new dashboard feature page”- Create or extend the owning feature under
src/features/or the owningpackages/feature-*package. - Keep the route file thin.
- Put page composition in
pages/. - Put server state in
queries.tsandmutations.ts. - 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”- Add or extend
query-keys.ts. - Add
queryOptions(...)factories inqueries.ts. - Keep raw write calls in
api.ts. - Wrap writes in
useMutationwith explicit invalidation inmutations.ts.
Start from:
Add a shared UI primitive
Section titled “Add a shared UI primitive”- Confirm it is generic and low-level.
- Confirm it does not import feature code.
- Use generic naming such as
Button,Card,Dialog,Tabs. - If the component is business-named, keep it in the owning feature.
Start from:
Add a reusable domain rule
Section titled “Add a reusable domain rule”- Keep it framework-light and runtime-light.
- Export pure functions, validators, policies, or interfaces.
- Do not import React, routes, server function wrappers, or Cloudflare env.
Start from:
Add a worker feature
Section titled “Add a worker feature”- Put orchestration in
features/{domain}/. - Keep runtime binding access in
infra/. - Keep the worker entrypoint thin.
- Use
shared/only for narrow worker-safe helpers and types.
Start from:
Add docs
Section titled “Add docs”- Decide whether the doc is canonical, operational, temporary, or local-only.
- Put it in the correct docs section immediately.
- Link local-only docs from
docs/README.mdif they need discoverability.
Start from:
What not to do
Section titled “What not to do”- Do not put business logic in route files.
- Do not put feature logic in
src/shared/orsrc/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 broadutils.tsfiles 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.
Lightweight scaffolding
Section titled “Lightweight scaffolding”For common addition paths, use the scaffold script:
pnpm scaffold dashboard-feature my-featurepnpm scaffold dashboard-feature my-feature --route my-featurepnpm scaffold domain-module my-domainpnpm scaffold worker-feature worker-ingestion-process linkingExpected output paths:
dashboard-feature→apps/dashboard/src/features/{feature}/...plus optional route files inapps/dashboard/src/routes/{-$locale}/(dashboard)/...domain-module→packages/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.
Validation before opening a PR
Section titled “Validation before opening a PR”Run the smallest relevant checks, then run architecture validation.
pnpm ci:archpnpm lintpnpm type-checkAlso run any feature-level or worker-specific validation needed for your slice. See 15-architecture-checks.md for what CI will enforce.
Current explicit exceptions
Section titled “Current explicit exceptions”These are real and documented. Do not copy them into new code unless the same exception is explicitly accepted.
workers/consumer-api/src/index.tsis still a documented monolith. New worker work should follow the worker structure guide instead of copying that layout.workers/rate-limiterremains a single-file Durable Object because the worker is intentionally small.- 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. - Root
plans/is deprecated. New plans go indocs/plans/.
When you encounter another exception, document why it differs and whether it is legacy-only or acceptable for new code.