Appearance
Repository Strategy โ Monorepo & Three-Layer Architecture ​
Status: Decided (records the locked structural decisions). The formal ADR is docs/internal/adr/0001-monorepo-three-layer-structure.md; the full platform context is pmo/platform-strategy.md.
Context ​
Heritage Community Hub must ship on three surfaces โ web, iOS, and Android โ from one codebase, built and maintained by a 1โ3 person team. The original question ("should this be multiple repos?") resolved into a clear structural decision: one monorepo, an API-first platform, decomposed on two axes. This document records that decision and the triggers that would cause us to revisit it.
Decision 1 โ API-first / headless platform ​
All business logic, data, auth, and rules live behind a single backend API (apps/api). Web and mobile contain only presentation + client state and call the platform through one typed client (packages/api-client). This is the keystone: build a feature once on the API and every surface consumes it, making web + 2 apps roughly 1.3ร the effort instead of 3ร.
Decision 2 โ Three conceptual layers ​
- Platform โ the core: backend API + web app + shared packages.
- Mobile apps โ iOS + Android clients (React Native + Expo).
- Features โ the capabilities the platform provides (calendar, announcements, marketplace, Pony Express, โฆ), consumed by all surfaces.
Decision 3 โ ONE monorepo, decomposed on two axes ​
A monorepo is one Git repo holding many independently bounded workspaces (each with its own package.json, build, and deploy). Separation happens on two axes:
- Axis 1 โ Surfaces (how users reach it): the deployable apps in
apps/. - Axis 2 โ Features (what it does): vertical slices (
features/<name>/) that repeat inside each surface, stitched together by shared types. A feature is not a top-level folder.
text
heritage-community-hub/ # ONE git repo
โโโ apps/ # SURFACES โ deployable applications
โ โโโ api/ src/features/{calendar,announcements,โฆ} # all logic โ cloud functions
โ โโโ web/ src/features/{calendar,announcements,โฆ} # React web client
โ โโโ mobile/src/features/{calendar,announcements,โฆ} # React Native (Expo) โ iOS/Android
โโโ packages/ # SHARED CODE โ imported by apps, not deployed alone
โ โโโ shared-types/ # domain types / API contracts (one Event type, used everywhere)
โ โโโ api-client/ # typed SDK web + mobile use to call the api
โ โโโ shared-utils/ # validation, RBAC helpers, formatting
โ โโโ shared-config/ # shared config/constants
โ โโโ ui/ # design tokens / shared design system
โโโ infrastructure/ # IaC (Bicep)
โโโ database/ # schema (design doc), migrations, seeds
โโโ docs/ # architecture, decisions (adr/), guides
โโโ pmo/ # planning & PM reference docsNote:
apps/*andpackages/*are planned, not yet created โ they are scaffolded in Phase 1. Thepackage.jsonworkspace scripts have no targets until then.
How one feature (e.g. Calendar) lives across the repo: logic in apps/api/src/features/calendar, UIs in apps/web/... and apps/mobile/..., all sharing the Event/RSVP types from packages/shared-types and calling the platform via packages/api-client. That shared-by-import flow is the entire reason for a monorepo.
Why monorepo (not multi-repo) for this goal ​
- Mobile and web share
shared-types+api-clientโ trivial in a monorepo, painful across repos (publishing/versioning a private package on every change). - Atomic commits: change an API contract and update all three clients in one PR.
- Tiny team; one CI config, one dependency graph, one VS Code workspace.
- The only defensible split โ mobile's separate release cadence โ is handled by a separate pipeline, not a separate repo.
When to revisit multi-repo ​
Split only when a real trigger appears:
- Team size โ 10+ developers stepping on each other.
- Independent deployment โ components that must deploy on genuinely independent schedules (beyond mobile's release cadence, which a separate pipeline already handles).
- Divergent tech stacks โ a component adopts a completely different technology.
- External access โ a third party needs access to one component only.
- Compliance โ separate security/audit boundaries are required.
Current situation favors monorepo: small team, tightly coupled API + clients, shared deployment cycle, single community focus. Recommendation: start monorepo; extract packages as they mature only if a trigger above is met.