Architecture overview
A pnpm + Turborepo monorepo. How it works, why it exists, where to change it:
| Piece | What | Where |
|---|---|---|
apps/web |
Next.js app (client components over a generated API client) | UI surfaces, panes, routing |
apps/api |
NestJS + Fastify | Controllers → services → repositories |
apps/worker |
Queue/outbox consumers (email retry, heartbeat) | Background behavior |
apps/mobile |
Expo React Native | Mobile loop |
apps/docs |
This site (Astro Starlight) | Documentation |
packages/schemas |
Zod contract registry — the API’s single source of truth | Request/response shapes, operation registry |
packages/api-client |
Generated from the registry; CI fails on drift (pnpm check:api) |
Never edited by hand |
packages/db |
Drizzle schema + migrations | Tables, migration harness |
packages/domain |
Pure shared logic (identifiers, CSV, markdown parser, filters) | Cross-platform rules |
packages/ui-web |
Primitives + Storybook | Buttons, dialogs, tokens usage |
packages/design-tokens |
Platform-neutral tokens | Color/type/spacing/motion |
Key patterns:
- One contract: web and mobile both consume the generated client; a contract change is a versioned, additive registry edit (ADR-012).
- Optimistic, revision-checked mutations everywhere a field is edited (ADR-013).
- Outbox events power notifications, realtime (SSE), and email — written atomically with the mutation (ADR-026).
- One URL, two presentations for task detail via Next intercepting/parallel routes (ADR-027).
Deep dive: SYSTEM_CONTEXT.