Repository navigation
Architecture Technique
Statut : 🟡 Brouillon · Màj : 2026-10-14 · Tickets : #125 #135 #162 #397
| Domaine | Choix | Pourquoi |
|---|---|---|
| Mobile | Expo (SDK courant), expo-router, TypeScript | React Native avec build cloud (EAS), mises à jour OTA, routage par fichiers |
| Carte | Mapbox (@rnmapbox/maps) |
Style personnalisé conforme à la maquette, marqueurs riches, geocoding, directions ; coût nul aux volumes du MVP |
| UI |
@gorhom/bottom-sheet, react-native-reanimated, composants maison |
Bottom sheet fluide sans dépendance à une lib UI lourde |
| Style | NativeWind, thème généré depuis tokens.json ; StyleSheet occasionnellement, pour les cas que NativeWind couvre mal (styles animés, couches Mapbox, valeurs calculées à l'exécution) |
Décision d'Ilan du 2026-09-24 (D-024) : standard mobile de l'agence, connu de l'équipe et rapide à écrire ; le preset Tailwind généré pourra servir aussi à l'admin web. Une seule source de valeurs, aucune valeur de style en dur. Écarté : StyleSheet partout avec un thème recopié à la main, qui avait divergé de tokens.json. Essai de compatibilité concluant (résultat sur #125) : NativeWind 4.2.7, dernière version stable, et Tailwind CSS 3.4.19, avec Expo SDK 57 et React Native 0.86 ; rendu et rechargement à chaud vérifiés en build de développement, sur simulateur iOS 26.4 et émulateur Android API 34. Écartée : la v5 de NativeWind, qui n'existait qu'en RC. Livré par la PR #132, fusionnée le 2026-09-25 ; précisions dans la section Mobile
|
| Données client | TanStack Query, Zustand pour l'état local, MMKV pour la persistance | Cache, retry, invalidation ; état de filtres et brouillons hors ligne |
| API | Node 22, Fastify 5, TypeScript, Zod | Validation par schéma, performances, typage partagé ; Express reste possible mais n'apporte rien de plus ici |
| Base | PostgreSQL 16 + PostGIS sur Cloud SQL, Drizzle ORM | Requêtes géographiques et filtres combinés natifs, relations, migrations versionnées ; Firestore écarté (une seule clause array-contains-any par requête, géo par geohash, pas de jointures) |
| Auth | Firebase Authentication | Apple, Google, e-mail gérés ; jeton vérifié par l'API |
| Push | Firebase Cloud Messaging via Expo Notifications | |
| Stockage média | Google Cloud Storage, URL signées, redimensionnement à l'upload | |
| Hébergement API | Cloud Run (europe-west9, Paris) | Sans état, scale à zéro puis horizontal, VPC vers Cloud SQL |
| Tâches planifiées | Cloud Scheduler → endpoints internes protégés | Recalcul nocturne, rappels de parcours, statuts stale
|
| Traduction | Modèle de langage via Vertex AI (projet GCP, région UE) | Traduction des parcours et des avis à la publication (route_translations) ; modèle choisi sur un test de 3 parcours (Gemini ou Claude selon disponibilité en UE) ; même contrat et même facture que GCP (décision d'Ilan du 2026-09-28) |
| E-mails | Brevo (UE) | E-mails transactionnels en français et en anglais : lien de connexion envoyé par l'API (lien Firebase généré par l'Admin SDK, modèle Widoo), confirmation de changement d'adresse, réponses du support ; offre gratuite au lancement (décision d'Ilan du 2026-09-28). Lien de connexion par Brevo à partir de la v0.4 ; d'ici là, l'e-mail standard de Firebase, pour les tests de l'équipe (décision d'Ilan du 2026-10-14, D-090) |
| Site web | Next.js (apps/web du monorepo) sur Cloud Run (europe-west9), détail dans Site-Web
|
Landing et pages de lien /r/:id (E-21) : rendu serveur pour l'aperçu des liens (Open Graph), React et types partagés avec l'API ; liens universels et App Links sur le même domaine ; mesure des clics vers les stores sans cookie, donc sans bannière de consentement (décision d'Ilan du 2026-09-28) |
| Météo | Open-Meteo | Gratuit, sans clé, cache 30 min |
| Abonnements | RevenueCat (react-native-purchases) |
Achats intégrés Apple et Google, reçus, renouvellements, restauration, webhooks vers l'API ; gratuit sous 2 500 $ de revenus mensuels |
| Analytics | PostHog (cloud UE) | Événements produit, funnels, feature flags |
| Crash | Sentry | Mobile et API |
| Admin web | Next.js (App Router), Tailwind, shadcn/ui thémé depuis tokens.json, même monorepo, même API, rendu des données côté client derrière connexion ; Cloud Run (choix d'Ilan du 2026-10-09, D-085, comme le site) |
Modération et gestion, voir Admin-Web |
| Monorepo | pnpm workspaces + Turborepo | Types et schémas partagés |
| CI | GitHub Actions | Lint, types, tests, build ; EAS pour les binaires |
Le vrai enjeu de scalabilité n'est pas la charge du MVP mais le modèle de données le jour où il y a vingt villes et des créateurs ; Postgres tient ce rôle sans réécriture.
widoo-app/
apps/
mobile/ Expo, expo-router
api/ Fastify, Drizzle, orchestration
admin/ Next.js (App Router), Tailwind, shadcn/ui
packages/
shared/ schémas Zod, types, énumérations, libellés FR, constantes
orchestration/ moteur de cohérence, pur, testé sans BDD
api-client/ client typé généré depuis les schémas Zod, utilisé par mobile et admin
infra/ Terraform ou scripts gcloud, Dockerfile API
docs/ plans, procédures
.github/ workflows, template de PR, CODEOWNERS
CLAUDE.md règles de travail des agents
SECURITY.md signalement des vulnérabilités
Langue : code, identifiants, commits et noms de fichiers en anglais ; wiki, issues, commentaires de PR et libellés utilisateur en français.
| Env | API | BDD | Mobile | Usage |
|---|---|---|---|---|
| local | pnpm dev |
Postgres+PostGIS en Docker | Expo Go ou dev build | développement |
| preview | Cloud Run par PR (optionnel v1) ou staging
|
Cloud SQL staging, données fictives | build EAS preview (canal staging) |
recette |
| production | Cloud Run widoo-api
|
Cloud SQL prod, sauvegardes quotidiennes, PITR | stores, canal production
|
Une fusion sur main n'autorise pas un déploiement en production. L'API staging se déploie automatiquement depuis main ; la production se déploie par une action manuelle d'Ilan ou de Paul, après un staging vert. Un rollback applicatif ne restaure pas une donnée supprimée.
Secrets dans Secret Manager, injectés par Cloud Run ; jamais dans Git ni dans l'app (seules les clés publiques Mapbox et Firebase sont embarquées, restreintes par bundle id).
| Env | PostHog | Sentry |
|---|---|---|
local (Metro, build development) |
aucune clé : rien n'est envoyé ; journal console seulement (EXPO_PUBLIC_ANALYTICS_DEBUG=1) |
aucun DSN par défaut ; pour tester un rapport, DSN du projet existant avec l'environnement development
|
| preview (staging) | projet Widoo — staging | projets widoo-mobile et widoo-api, environnement preview (mobile) et staging (API) |
| production | projet Widoo — production | mêmes projets, environnement production
|
-
Le développement local n'envoie aucun événement produit. Une clé PostHog dans
apps/mobile/.env.localn'est pas prévue ; et comme ce fichier est relu à l'exécution même quand la variable est vidée dans le shell, l'app ignore la clé en développement : seul un buildpreviewouproductioncrée le client PostHog. Pour vérifier un événement, le journal local suffit (nom, propriétés, consentement) ; l'arrivée réelle dans PostHog se vérifie sur un buildpreview, dans le projet staging. - Staging et production sont deux projets PostHog distincts, dans la même organisation PostHog Cloud UE, chacun avec les réglages obligatoires de Securite-et-RGPD (« Discard client IP data », pas de PostHog AI). Les tableaux de Analytics se construisent sur le projet production ; le projet staging sert à la recette et peut être vidé sans perte.
-
Sentry garde un projet par surface (
widoo-mobile,widoo-api) et sépare les environnements par son champenvironment, toujours renseigné (EXPO_PUBLIC_APP_ENVposé pareas.jsonpour le mobile,SENTRY_ENVIRONMENTpour l'API). Les alertes et le suivi des régressions filtrent surproduction. -
Les clés vivent dans l'environnement EAS du profil (
preview,production) pour le mobile et dans Secret Manager pour l'API ; jamais dans un fichier versionné, ni dans l'environnement EASdevelopment. La clé PostHog et le DSN Sentry sont publics par nature (écriture seule) : le risque n'est pas la fuite, c'est le mélange des données.
Pourquoi : les hypothèses du MVP se mesurent sur les funnels et les personnes de PostHog, qui n'ont pas de filtre d'environnement fiable : un seul événement de développement ou de recette fausse un funnel, crée une personne et touche les feature flags, et ne se retire qu'à la main. Sentry, lui, traite l'environnement comme une dimension de premier rang (issues, alertes, releases) : un projet par environnement doublerait les vérifications de D-019 sans rien séparer de plus.
Écarté :
- un projet PostHog de développement partagé : les sessions de chacun s'y mêlent sans rien valider que le journal local ne montre déjà, et il faudrait tenir ses réglages RGPD pour un projet qui ne sert à aucune mesure ;
- la clé de staging en local : c'est le cas relevé dans #162, qui mêle les sessions de développement à la recette et, par erreur de copie, à la production ;
- un seul projet PostHog pour staging et production, filtré par une propriété d'environnement : chaque tableau et chaque cohorte devrait porter le filtre, et un oubli fausse la mesure ;
- un projet Sentry par environnement : voir la raison ci-dessus.
- REST JSON, versionnée par préfixe
/v1. Voir API. - Validation Zod à l'entrée et à la sortie (
fastify-type-provider-zod), OpenAPI généré. - Auth : en-tête
Authorization: Bearer <firebase id token>, vérifié par le SDK Admin, utilisateur chargé ou créé à la volée. Endpoints publics sans jeton. - Erreurs : format unique
{ code, message, details }avec codes stables. - Limitation de débit par IP et par utilisateur (
@fastify/rate-limit), plus stricte sur la création de lieux et les signaux. - Journalisation structurée (pino), sans donnée personnelle ; identifiant de requête propagé.
- Structure
app/(tabs)/index|routes|profile, modalesapp/route/[id],app/plan/[id],app/go/[id],app/create/*,app/auth. - Hors ligne : consultation du cache TanStack Query et brouillons persistés ; création complète requiert le réseau (géocodage, calcul).
- Performance : première carte affichée sous 3 s en 4G, liste virtualisée, images via
expo-imageavec tailles servies adaptées. - Deep links :
widoo://route/<id>ethttps://widoo.app/r/<id|share_token>(App Links et Universal Links). - i18n :
i18next, français et anglais livrés (D-062), clés en anglais ; langue du téléphone par défaut, choix appliqué sans redémarrer ;users.localepour les textes envoyés par le serveur. - Style (D-024) : NativeWind, livré par #125 (PR #132, fusionnée le 2026-09-25).
- Une seule source de valeurs : le paquet
packages/tokensgarde une copie detokens.json, tenue identique à celle du wiki et vérifiée en CI, et en génère le preset Tailwind et le module typé@widoo/tokens, qui porte les valeurs et les correspondances (familles, catégories de lieux, icônes de filtre, pastilles, points d'ambiance, mouvement). Le preset remplace les valeurs par défaut de Tailwind, et un lint bloquant refuse toute valeur de style en dur. - Le preset de NativeWind garde ses plugins, qui traduisent les classes en styles React Native, mais son thème est écarté : seuls les tokens restent.
- Ombre :
shadow-tabbarest posée par leboxShadownatif, avec la valeur du token, et non par une classe : NativeWind ferait d'une classe d'ombre uneelevationAndroid, différente des maquettes. - Mouvement : les animations passent par Reanimated et les constantes générées de
mappings.motion, jamais par les classestransition-*ouanimate-*: une telle classe, activée après le premier rendu, fait remonter le composant par NativeWind. - Un
Pressablene prend pas destyleen fonction (({ pressed }) => …) : NativeWind ne l'applique plus. L'état d'appui passe par les variantesactive:ou par un état React. - Avec pnpm,
react-native-css-interopest une dépendance directe de l'app : NativeWind en injecte les imports dans chaque fichier. - Interligne sur Android : le composant
Textgarde l'interligne de la police plutôt que celui des tokens, qu'il applique sur iOS, parce qu'Android 14 rogne les jambages à 150 % de texte. La règle reste à valider dans Direction-Artistique par #135. -
StyleSheet: à la livraison, un seul cas, le voile animé du bouton (Reanimated), avec la couleur du thème généré. - Des composants de base (texte, icône, bouton, chip, pastille, point d'ambiance, pastille d'étape, card) portent les règles d'accessibilité de Direction-Artistique ; les écrans les assemblent.
- Une seule source de valeurs : le paquet
- Accessibilité : labels sur tous les contrôles, contraste AA, tailles dynamiques.
- TypeScript strict partout, ESLint, Prettier.
- Tests : Vitest (orchestration, API avec base de test), React Native Testing Library pour les composants critiques (filtres, éditeur), Maestro pour deux parcours de bout en bout (découverte → fiche ; création → soumission).
- CI bloquante sur chaque PR : lint, types, tests, build API ; build EAS preview sur
main. - Taille des changements, découpage et fusion des PR : règles de travail dans
CLAUDE.mddu dépôt.
Sentry (mobile et API), logs Cloud Run, alertes sur taux d'erreur 5xx et latence p95 de la recherche (> 800 ms). Tableau PostHog pour les KPI de Analytics.
Cloud SQL petite instance ~30 à 60 €, Cloud Run ~0 à 20 €, Mapbox et Firebase dans les paliers gratuits, PostHog et Sentry gratuits aux volumes initiaux, EAS selon formule. À réévaluer à la validation du MVP.
Produit
- Vision-Produit
- Perimetre-MVP
- Personas-et-Parcours-Utilisateurs
- Ecrans
- Filtres-et-Recherche
- Orchestration
- Creation-et-Moderation
- Admin-Web
- Compte-et-Monetisation
- Direction-Artistique
Technique
- Modele-de-Donnees
- Architecture-Technique
- Site-Web
- API
- Securite-et-RGPD
- Conditions-d-utilisation
- Politique-de-confidentialite
- Analytics
Pilotage