Skip to content

Architecture Overview ​

A concise map of how Heritage Community Hub is structured. For the full strategy, decisions, and roadmap see pmo/platform-strategy.md. For the repository/monorepo rationale see REPOSITORY_STRATEGY.md. Decisions are recorded in docs/internal/adr/.

The shape in one sentence ​

One API-first platform serves three thin clients — web, iOS, and Android — from a single monorepo, with features built once on the backend and consumed everywhere through shared, typed code.

The three layers ​

  1. Platform (the core)apps/api (all business logic, data, auth, rules) + apps/web + shared packages/*.
  2. Mobile appsapps/mobile (React Native + Expo → iOS + Android).
  3. Features — vertical slices (calendar, announcements, marketplace, …) repeated inside each surface and stitched together by shared types.
text
            ┌───────────────┐   ┌───────────────┐   ┌───────────────┐
            │   apps/web     │   │  apps/mobile   │   │  (future:      │
            │  (React/TS)    │   │  (RN + Expo)   │   │   admin, etc.) │
            └───────┬───────┘   └───────┬───────┘   └───────┬───────┘
                    │   packages/api-client (typed SDK)      │
                    └───────────────┬───────────────────────┘

                         ┌────────────────────┐
                         │      apps/api       │  ← all logic, auth, rules, data access
                         │  (single backend)   │
                         └─────────┬──────────┘

                         managed cloud database
   shared everywhere:  packages/shared-types · shared-utils · shared-config · ui

Why API-first ​

Web and mobile hold only presentation + client state; everything authoritative lives behind the API. A feature (e.g. Calendar) is implemented once in apps/api/src/features/calendar, exposed through packages/api-client, and rendered by web and mobile UIs sharing the Event/RSVP types from packages/shared-types. This is what makes web + 2 apps ~1.3× effort instead of 3×, and it keeps authorization server-side (clients never self-authorize — see two-plane RBAC below).

Cross-cutting foundations ​

  • Identity & accounts — closed, member-only community; minister-approved Family Groups; adults use Apple/Google social login; children use parent-managed (non-social) credentials. (ADR 0003, 0007)
  • Authorization — two planes: infrastructure access (Entra/Azure RBAC) vs. application authorization (community roles, enforced in the API). (ADR 0006)
  • Observability — four layers: app audit log, app telemetry, infra monitoring/alerts, auth-provider logs. (ADR 0005)
  • Notifications — email + SMS + in-app + push, with priority levels.

Build order (so web + mobile stay cheap) ​

  1. Platform foundation — API skeleton + auth + RBAC + shared-types + api-client.
  2. Web client shell consuming the API.
  3. Calendar end-to-end on web (proves the API-first pattern).
  4. Announcements / notifications.
  5. Mobile app (Expo) reusing api-client — now mostly UI.
  6. Remaining feature modules (Small Groups, Homeschool, Marketplace) as platform modules.
  7. Signature features (Pony Express, ride share, sister communities) post-MVP.

Status ​

Decision layer complete. All 24 ADRs (0001–0024) are Accepted. The technology stack is locked:

ConcernDecisionADR
Monorepo toolingpnpm workspaces + TurborepoADR 0001
MobileReact Native + Expo managed workflow, EAS BuildADR 0002
AuthenticationClerk (Apple/Google social login) for adults; parent-managed (Argon2id) for childrenADR 0003
ComputeAzure Container Apps (containerised Node.js — Docker)ADR 0024
DatabasePostgreSQL on Azure DB for PostgreSQL Flexible ServerADR 0024
StorageAzure Blob Storage (active); Cloudflare R2 as cost-triggered swapADR 0010 / ADR 0024
NotificationsTwilio (SMS), Expo push (mobile), SendGrid (email), Postgres table (in-app)ADR 0013
CI/CDGitHub ActionsADR 0004

apps/* and packages/* are planned and will be scaffolded during Phase 1 (ADO AB#3074). For the authoritative delivery plan see pmo/platform-strategy.md.

Heritage Community Hub — Internal. Access restricted via Cloudflare Access + Entra ID.