Skip to content

Architecture

Directory Structure

/
├── products/ # Product-centric apps
│ └── <product>/
│ ├── shared/ # Shared types, utils, UI for this product
│ ├── web/ # Vite + React → Cloudflare Pages
│ ├── backend/ # Cloudflare Workers API
│ └── mobile/ # (optional) React Native / Expo
├── packages/ # Cross-product shared packages
│ ├── auth/ # Auth interfaces + middleware
│ └── db/ # Database abstraction (Drizzle-based)
├── tooling/ # Shared configs (each is a package)
│ ├── eslint/ # Flat ESLint config
│ ├── tsconfig/ # Base tsconfig files
│ └── prettier/ # Prettier config
├── docs/ # This documentation site
└── scripts/ # Generator scripts

Key Decisions

Package Scope

All internal packages use the @repo/* scope (e.g., @repo/auth, @repo/example-web).

Tooling as Packages

ESLint, TypeScript, and Prettier configs are each their own workspace package. Any workspace can depend on them via workspace:* protocol.

Database Abstraction

@repo/db uses a factory pattern with Drizzle ORM. The factory accepts a provider config and returns the appropriate adapter. Schemas are defined per-product; the driver is swappable.

Auth Contract

@repo/auth defines interfaces (AuthProvider, Session, User) and a generic middleware function. Products choose their own implementation.

Turborepo Pipelines

  • build — depends on upstream builds, outputs dist/
  • dev — no caching, persistent
  • test — depends on upstream builds
  • lint — depends on upstream builds
  • typecheck — depends on upstream builds

Deployment

All deployments are automated via GitHub Actions CI:

  • Web apps — Built with Vite, deployed to Cloudflare Pages (wrangler pages deploy)
  • Backend APIs — Cloudflare Workers deployed via wrangler deploy
  • Docs — Built with Astro, deployed to Cloudflare Pages

The CI pipeline uses Turborepo’s change detection (--filter="...[$BASE_SHA]") to only deploy targets affected by each push. PR previews get unique branch URLs; merges to main deploy to production.