Monorepo overview
How the albertonline.org monorepo is laid out — apps, packages, tooling, and the shared-code philosophy.
The repository is a pnpm + Turborepo monorepo. Everything the product needs — the web app, its companion apps, the mobile client, and every shared library — lives in one tree, type-checked and linted as a whole.
albertonline.org/
├── apps/ # deployable applications (Next.js, Expo, Effect servers, …)
├── packages/ # shared libraries consumed via workspace: / @repo/*
├── tools/ # vendored references (effect-smol) + standalone tooling
├── docs/ # design specs / ADRs (this Fumadocs site lives in apps/docs)
├── obsidian-vault/ # Zettelkasten engineering notes
├── turbo.json # task graph + caching
├── pnpm-workspace.yaml # workspace globs + the version catalog
└── package.json # root scripts (dev, build, checks, db)Two top-level buckets
apps/*
18 deployable or runnable apps — the main web suite plus companion apps (chess, e-commerce, video, docs).
packages/*
~50 shared libraries — the API, auth, db, design system, Effect infrastructure, and domain services.
apps/*are the things you run or ship. The primary product isapps/web; the rest are companion surfaces and services.packages/*are shared code, imported as@repo/<name>. An app should reach for a package before writing something bespoke — the whole point of the monorepo is that the API layer, auth, database, UI, and Effect plumbing are written once and reused everywhere.
Tooling
| Concern | Tool |
|---|---|
| Package manager | pnpm 11 workspaces (nodeLinker: hoisted) |
| Task runner / cache | Turborepo (turbo.json) |
| Framework | Next.js 16 (App Router, Turbopack always) + React 19 |
| Types | tsgo — the native TypeScript 7 preview (check-types) |
| Lint | oxlint (pnpm lint) + the Effect JS plugin |
| Format | oxfmt (pnpm format:check / format:fix) |
| React health | react-doctor (must stay at 0 errors) |
| Tests | Vitest (node + browser modes) + Playwright E2E |
| Dead code / dupes | fallow (pnpm fallow) + knip |
| Effect system | Effect v4 (4.0.0-beta.94) |
The version catalog — one source of truth
Dependency versions are centralised in the catalog: block of
pnpm-workspace.yaml. A package depends on
"next": "catalog:" rather than pinning a number, so an upgrade happens in one
place. Doc citations of a version are gated by pnpm check:doc-versions, so
READMEs can't drift from the catalog.
Always use Turbopack for next dev / next build — every app's dev/build script already
passes --turbopack. A bare next dev falls back to webpack and is unsupported here.
Shared-code philosophy
- Effect v4 everywhere it earns its keep — services are
Context.Serviceclasses with a staticlayer; errors are typed (Schema.TaggedError); every value crossing a boundary (HTTP body, env var, DB row, RPC payload) is decoded with aSchema, never cast. - Strict TypeScript, no escape hatches — no
any, noas(exceptas const), no!, no@ts-*. A type error is a signal to fix the source of the unknown shape, not to suppress it. - One API boundary — apps talk to the backend through the
Effect v4 RPC layer in
@repo/api, not ad-hoc fetches.
Next: read the architecture guide, or jump to local development to get an app running.