Monorepo with real contract boundaries
Always in
Eight first-class packages, and a contract package that makes duplicate truth a compile error rather than a code-review convention.
- Bun workspaces with eight packages: shared, database, backend, worker, scheduled-jobs, api-client, frontend-app, frontend-public. Deploy the ones your domain needs; the boundaries hold either way.
- A shared contract package — enums, Zod schemas, error classes — imported identically by the backend, the worker, and the jobs. One definition, three consumers, no drift.
- Module-per-domain backend layout (src/modules/<name>/{routes,service}.ts) with constructor-injected services wired once at composition time. No globals, no service locator.
- TypeScript strict plus noUncheckedIndexedAccess across every package, with project references so a build order exists on day one.