Skip to content

Code Placement Guide

Migrated from root technical docs.

Answer: “If I am adding X, where does it go?”

For structural context see 00-repo-map.md. For dependency rules see 03-dependency-rules.md. For category definitions (app / worker / feature / domain / platform / contract / shared UI) see 02-package-categories.md.


Use these before creating a file.

AreaOwnsPut code here when…Keep out
apps/dashboardDashboard routes, page composition, auth, server functions, app-shell wiringthe code is dashboard-specific or route-ownedgeneric shared libraries and worker runtime code
apps/marketingPublic marketing sitethe code is public marketing content/pagesbackend worker runtime code
apps/docsPublic documentation sitethe code is docs site content/pagesbackend worker runtime code
packages/feature-*Reusable dashboard feature queries, mutations, models, feature-safe UI slicesthe code is reused across multiple dashboard screens and should stay buildable outside the app shellroute/page composition and direct DB/runtime ownership
packages/platform-*Cross-feature platform infrastructurethe code supports multiple features or apps without being domain-specificdashboard-only page logic
packages/domain-*Pure business/domain logicthe code is framework-light, portable, and business-centeredCloudflare runtime adapters and UI wiring
workers/*Worker entrypoints, worker features, runtime adaptersthe code depends on queues, Durable Objects, worker env bindings, or worker orchestrationbrowser-facing app code
  1. Keep user-facing deployable entrypoints in apps/.
  2. Keep reusable libraries in packages/.
  3. Keep dashboard page composition in apps/dashboard, even when the data layer lives in packages/feature-*.
  4. Keep worker domain logic in workers/*/src/features/ and worker adapters in workers/*/src/infra/.
  5. Move code to packages/ only when there is a real shared owner, not a hypothetical future reuse case.
What you are addingWhere it goesNotes
New dashboard page (UI)apps/dashboard/src/features/{domain}/pages/Route file is thin; page component lives here
New TanStack route fileapps/dashboard/src/routes/{-$locale}/(dashboard)/Locale-prefixed, file-based
Route loaderInside the route fileKeep loaders small; delegate to server functions
Server function (createServerFn)apps/dashboard/src/server/functions/{domain}/index.tsGrouped by domain
Dashboard UI component (domain-specific)apps/dashboard/src/features/{domain}/components/Owned by the feature
Generic UI primitive (button, input, table, skeleton)apps/dashboard/src/shared/ui/shared UI category; no feature imports allowed
App-shell layout component (dashboard page header, shell framing)apps/dashboard/src/app/layouts/app-shell category; may be consumed across features
Shared UI primitive (used by 2+ features)apps/dashboard/src/shared/ui/shadcn-style; no feature imports allowed
Business-named component (PeopleTable, PublicationsFilters, SyncStatusBadge)owning feature components/feature-owned UI; may depend on shared primitives
Shared app-wide hook (used by 2+ features)apps/dashboard/src/hooks/Only if genuinely cross-feature
Domain hook (used by 1 feature)apps/dashboard/src/features/{domain}/hooks/Keep it in the feature
TanStack Query keyspackages/platform-query/src/ for shell-level; packages/feature-{domain}/src/query-keys.ts for domainDo not scatter inline
TanStack Query options / hookspackages/feature-{domain}/src/queries.tsCo-located with the feature
API transport wrapper (calls server fn)packages/feature-{domain}/src/api.tsDynamic import() + call() pattern
Business/domain rulepackages/feature-{domain}/src/ or apps/dashboard/src/features/{domain}/Prefer extracted package if reused across surfaces
Zod schema for API input/outputpackages/schemas/src/index.tsShared schemas only
TypeScript interface / shared typepackages/types/src/Interfaces only; no logic
DB schema changepackages/db/src/schema.ts → run pnpm db:drizzle:generateAll 5 shards share the same schema
D1 migration SQLmigrations/ + Drizzle migration files in packages/db/drizzle/Run schema gen first
Cloudflare binding or runtime adapterapps/dashboard/src/server/ or packages/platform-cloudflare/src/Never in client-safe folders
Auth logic (session, CSRF, OAuth)apps/dashboard/src/server/auth/Do not split auth across lib
Ingestion pipeline featureworkers/ingestion-process/src/, workers/ingestion-orchestrator/src/, or packages/platform-ingestion/src/Worker entrypoints own routing; shared ingestion runtime lives in @platform/ingestion
Worker infrastructure adapterworkers/*/src/infra/Keep runtime adapters with the owning worker
Public API routeworkers/consumer-api/src/Read-only; no writes
OpenAPI / integrations contractpackages/api-contract/src/Versioned; generates docs
Integration runtime helperpackages/integration-core/src/Shared across integration flows
Event bus event or handlerpackages/platform-events/src/Typed events, registry, cache invalidation
i18n locale stringapps/dashboard/src/locales/{locale}/ → synced via packages/platform-i18nRun locale key check after adding
E2E testtests/features/{domain}.mjs or tests/features/{domain}/cases/Node harness, .mjs files
Unit/integration test (Vitest)Co-located with source file as {file}.test.ts(x)Same directory as the file under test
Deploy scriptscripts/deploy/Bash; use scripts/lib/common.sh helpers
DB or migration scriptscripts/db/Bash or .mjs
CI orchestration scriptscripts/test/ci.mjsNode ESM
Operational runbookapps/docs/src/content/docs/guides/Public-facing; use for production ops
Internal runbook / ADRdocs/Internal; not published
In-flight design docplans/Temporary; delete when work lands
Admin CLI commandcli/src/commands/legaciti binary
Ingestion tracing primitivespackages/platform-ingestion/src/infra/telemetry/tracing.tsShared tracing helpers reused by split ingestion workers

Is it domain/business named or tied to one feature's data model?
YES → apps/dashboard/src/features/{domain}/components/
NO → Is it app-shell framing (header, dashboard page intro, nav chrome)?
YES → apps/dashboard/src/app/layouts/
NO → Is it a low-level primitive (button, card, input, dialog, table primitive)?
YES → apps/dashboard/src/shared/ui/
NO → keep with current owner and document a narrow exception
Is it a query key or retry config shared across multiple features?
YES → packages/platform-query/src/
NO → Is it query options/hooks for one domain?
YES → packages/feature-{domain}/src/queries.ts
NO → Is it a shell-level key (top nav, global state)?
YES → packages/platform-query/src/shell-query-keys.ts
Does it touch Cloudflare bindings, D1, KV, auth sessions, or SES?
YES → apps/dashboard/src/server/{...}
NO → Is it a pure utility?
YES → apps/dashboard/src/lib/ (app helpers) or packages/utils/ (truly shared)
Is it a TypeScript interface only (no logic)?
YES → packages/types/src/
Is it a Zod schema for request/response validation?
YES → packages/schemas/src/index.ts
Is it a DB column/table shape via Drizzle?
YES → packages/db/src/schema.ts

If you are adding…Put it hereWhy
New dashboard page routeapps/dashboard/src/routes/{-$locale}/(dashboard)/route declaration stays app-local
Dashboard page componentapps/dashboard/src/features/{domain}/pages/page composition belongs to the app shell
Dashboard-only toast hookapps/dashboard/src/features/{domain}/hooks/tied to one dashboard feature owner
Reusable query options for publicationspackages/feature-publications/src/queries.tsreusable feature data layer
Query key shared across multiple featurespackages/platform-query/src/platform-level concern
Pure publication normalization helperpackages/utils/src/framework-light shared utility
Shared affiliation policypackages/domain-people/src/domain logic, not UI wiring
Cloudflare D1 adapter for a workerworkers/*/src/infra/worker runtime ownership
Queue-consumer business flowworkers/ingestion-process/src/, workers/ingestion-orchestrator/src/, or packages/platform-ingestion/src/worker entrypoints own routing; shared ingestion runtime lives in @platform/ingestion
Public API route handlerworkers/consumer-api/src/worker-owned deployable surface

  • Do not add Cloudflare bindings access to src/lib/ or src/features/
  • Do not statically import server functions from client code (Oxlint blocks this)
  • Do not let shared UI components import from feature modules (Oxlint blocks this)
  • Do not edit apps/dashboard/src/routeTree.gen.ts — auto-generated by TanStack Router
  • Do not add business logic to route files; delegate to feature page components
  • Do not centralize code into lib “just in case”; keep it in the feature until it is genuinely shared
  • Do not add new production code to workers/my-api/ — it is DEPRECATED
  • Do not add dashboard page components or route-option bundles to packages/feature-* — dashboard pages belong in apps/dashboard/src/features/{domain}/pages/