My App

ADR-01 — Séparation apps PWA et Dashboard

Deux Next.js séparés (apps/pwa + apps/dashboard) plutôt qu'un seul avec route groups

Statut : Accepté Date : 2026-04 Sujet : Structure monorepo — combien d'apps web

Contexte

Bell a deux interfaces :

  1. Une PWA Guest accessible par lien unique envoyé par mail, contrainte 430px, mobile-first, service worker, check-in flow
  2. Un Dashboard Staff/Admin desktop-first avec sidebars, tables denses, graphiques analytics

Elles partagent la base (Better Auth, packages/api via Eden Treaty, packages/ui, packages/db) mais ont des besoins UX radicalement différents.

Dans l'ancien projet, le front guest était en Expo React Native Web (apps/native) et le dashboard en Next.js (apps/web) — deux stacks complètement différentes. On garde le principe de deux fronts séparés, mais cette fois sur la même stack Next.js.

Alternatives considérées

Option 1 — Un seul apps/web avec route groups

Une seule app Next.js avec (pwa), (dashboard), (admin), (auth) dans le même src/app/.

Pour :

  • Un seul package.json, un seul next.config, un seul middleware
  • Code 100 % colocalisé
  • Déploiement unique

Contre :

  • Bundle commun qui charge des deps PWA (vaul, service worker, manifest) aussi dans le dashboard, et l'inverse
  • Middleware auth qui doit jongler entre role=guest (redirect vers check-in flow) et role=staff (redirect vers /dashboard) avec beaucoup de cas spéciaux
  • Cycles de déploiement couplés — un hotfix dashboard bloque une feature guest
  • PWA service worker scope s'étale sur tout le domaine, ce qui complique la mise en cache
  • Performance Lighthouse dégradée sur la PWA (code dashboard embarqué via chunks lazy mais toujours dans le bundle)

Option 2 — Deux apps séparées (retenu)

apps/pwa (guest) + apps/dashboard (staff/admin). Partage via packages/*.

Pour :

  • Bundles séparés et optimaux pour chaque cible
  • Middleware auth simple : chacun vérifie un role précis et redirige vers son propre /auth
  • Déploiements indépendants — on peut hotfix l'un sans toucher l'autre
  • Service worker PWA scope sur bell-app.hoaiy.com uniquement, dashboard sur bell-staff.hoaiy.com
  • Tooling PWA (Vaul, manifest, meta tags iOS) n'impacte pas le dashboard
  • DX meilleure : équipe guest / équipe staff peuvent bosser sans se marcher dessus

Contre :

  • Deux package.json, deux next.config, deux déploiements
  • Duplication mineure de la glue (providers React, theme, layout root) — négligeable

Décision

Deux apps Next.js 15 séparées :

  • apps/pwa — PWA Guest, mobile-first, contrainte 430px, Vaul pour les modals natif-like
  • apps/dashboard — Staff + Admin, desktop-first, sidebar, tables, charts

Partage via le monorepo :

packages/
├── api          → Eden Treaty app (Elysia) + types TypeBox partagés
├── auth         → Better Auth config + mail templates
├── db           → Drizzle schema + migrations
├── ui           → shadcn components + design tokens Bell
├── env          → Validation env vars (Zod)
└── config       → tsconfig/biome partagés

Client Eden Treaty setup dans chaque app (apps/pwa/src/lib/eden.ts, apps/dashboard/src/lib/eden.ts) avec le même packages/api importé.

Routing et domaines

AppDevProd
apps/pwalocalhost:3001bell-app.hoaiy.com
apps/dashboardlocalhost:3002bell-staff.hoaiy.com
apps/server (Elysia)localhost:3000bell-api.hoaiy.com
apps/fumadocslocalhost:4000bell-docs.hoaiy.com (privé)

En dev, chaque app web utilise un proxy Next.js rewrites vers l'Elysia (/api/*localhost:3000/*) pour éviter le cross-origin. Voir ADR-02.

En prod, cookies signés avec domain: .hoaiy.com pour SSO entre les subdomains app et staff.

Conséquences

Positives :

  • Chaque app est optimisée pour son usage, pas de compromis
  • Déploiement sélectif (canary staff sans impact guest)
  • Onboarding dev plus simple — on dit "tu bosses sur guest" et la zone de code est claire
  • Service worker PWA propre

Négatives :

  • Maintenance de deux next.config.ts, deux middleware.ts — mineur
  • Si on ajoute un composant partagé, il doit aller dans packages/ui et pas dans apps/*/components — discipline à tenir

Métriques à surveiller

  • Taille du bundle JS initial de chaque app (cible PWA : < 200 KB gzipped, dashboard : < 400 KB)
  • Lighthouse PWA Mobile (cible : > 90 sur tous les axes)
  • Temps de build de chaque app (cible : < 60 s en prod)
  • Nombre de composants dupliqués entre les deux apps (cible : 0 — si on dup, on refactor vers packages/ui)

On this page