Skip to content

Monorepo structure

Why the Perci Platform lives in a single repository, how it is laid out, and the key decisions behind that layout.

Why a monorepo

Four separate repositories were consolidated into one to get:

  1. Unified CI/CD across frontend and backend.
  2. Shared code via local path dependencies (no git submodules).
  3. Atomic cross-cutting changes in a single PR across apps.
  4. Simpler onboarding: one git clone, one tooling setup.
  5. Code reuse between the Members and Clinicians apps via shared packages.
  6. Consistent tooling: standard linting, formatting and testing everywhere.
  7. AI-agent friendliness: tools can see the whole codebase at once.

History

The repo was assembled from four originals, with full git history preserved via git subtree:

  • Backend (Node.js): perci-platform-backend
  • Members app (Flutter web): perci-platform
  • Clinicians app (Flutter web): perci-platform-clinicians
  • Shared Flutter library: perci-platform-shared

The original repositories are archived (read-only). The branch flow today is develop for staging and main for production (see Branching & releases).

Structure

perci-platform-monorepo/
├─ apps/
│  ├─ perci-platform-members/      # Flutter web app for members
│  ├─ perci-platform-clinicians/   # Flutter web app for clinicians
│  └─ perci-platform-backend/      # Backend (Node.js, TypeScript)
│     ├─ functions/                # Firebase Functions (Express apps)
│     ├─ medplum/bots/             # FHIR automation bots
│     └─ web-professionals/        # React clinician portal (Vite)
├─ packages/
│  ├─ perci-platform-frontend-shared/  # Shared Flutter components & utilities
│  ├─ ff_commons/                      # FlutterFlow common utilities
│  └─ ff_theme/                        # FlutterFlow theming
├─ .github/workflows/              # CI/CD workflows (path-filtered)
├─ melos.yaml                      # Melos config (Dart/Flutter only)
├─ pubspec.yaml                    # Root Melos tooling deps
└─ .fvmrc                          # Flutter version (FVM)

Key design decisions

Three shared packages, not one. The original perci-platform-shared was split into perci-platform-frontend-shared (app-specific shared code), ff_commons (FlutterFlow utilities) and ff_theme (FlutterFlow theming) for better modularity and reuse.

Backend kept as an app. apps/perci-platform-backend holds three distinct Node.js projects: functions/ (Firebase Functions, multiple Express apps), medplum/bots/ (FHIR automation) and web-professionals/ (React + Vite portal).

Tooling isolation. Melos manages only the Dart/Flutter packages; the Node.js backend uses pnpm independently. Dart and Node tooling are intentionally not unified, which keeps clean boundaries.

Gotchas

  • Each Flutter app's pubspec.yaml name: is what Melos scopes on (perci_platform, perci_clinical); use those with melos exec --scope <name>.
  • The backend has three separate Node.js projects, each needing its own install; Firebase emulators must be running before backend debugging.
  • Run dart pub get at the root before any melos command, and do not try to manage the Node.js projects with Melos.

For day-to-day setup and commands see Running locally; for CI see GitHub workflows.