Skip to content

ADR-0004: Monorepo layout — apps, packages, infra, scripts, specs

Status: Accepted
Date: 2026-05-02
Owner: @satya

Treeper has three runtimes (Flutter mobile, NestJS API, Python workers) plus shared decisions and contracts. We can either:

  • One repo per runtime (multi-repo).
  • One repo for everything (monorepo).

For a small team, multi-repo means PRs cross repos for almost every change that touches a contract. Monorepo wins.

A single Git repo with this layout:

treeper/
├── apps/
│ ├── mobile/ Flutter app
│ ├── backend/ NestJS API
│ └── workers/ Python (FastAPI)
├── packages/ Shared TS types / contracts (planned)
├── infra/
│ ├── docker/ docker-compose for local dev
│ └── coolify/ deploy templates
├── scripts/ Cross-app helper scripts
├── specs/ SDD source of truth
├── PRODUCT.md
└── README.md

Tooling stays per-app (no Nx / Turborepo at v0). Each apps/* directory has its own README with run / build / test commands.

OptionWhy not
Multi-repoCross-repo PRs for contract changes; harder to enforce SDD link from PRs.
Nx / Turborepo / pnpm workspacesWorth it once we have shared TS code; premature for three foreign runtimes.
  • One PR can carry mobile + backend + worker + spec changes together.
  • A single specs/ is the cross-runtime source of truth.
  • docker compose up boots the whole backend stack from one place.
  • CI matrix gets bigger; we’ll need path-filtered jobs.
  • Repo size grows over time (Flutter pub cache, etc.); manage with a strict .gitignore.
  • Path-filtered CI when we start adding GitHub Actions.
  • Add packages/contracts/ once the API surface is large enough that the mobile app benefits from a generated client.