# ARCHITECTURE — Sargasse-Sentry ## Stack technique | Couche | Technologie | Rôle | |---|---|---| | Backend | Symfony (PHP) | API JSON, crons, pipeline | | Frontend | React SPA | Interface utilisateur | | Maps | MapLibre GL JS + react-map-gl | Rendu cartographique | | Base de données | PostgreSQL + PostGIS | Données géospatiales | | Vector tiles | Tippecanoe (génération) | Tiles statiques pré-générées | | Cache | Redis | Scores, résultats API | | Reverse proxy | Traefik | Routage multi-tenant, SSL | | Runtime PHP | FrankenPHP | Remplace php-fpm + nginx | | Conteneurisation | Docker | Isolation, déploiement | --- ## Infrastructure ### Hébergement VPS personnel, architecture **multi-tenant Docker + Traefik + FrankenPHP** (configuration existante partagée entre projets). ``` Internet └── Traefik (reverse proxy, SSL Let's Encrypt) ├── sargasse-sentry.tld → container Symfony/FrankenPHP ├── [autres projets] └── ... Containers Sargasse-Sentry : ├── frankenphp (Symfony API + worker cron) ├── postgres (PostgreSQL + PostGIS) └── redis (cache) ``` ### CDN (optionnel, sans coût) **Cloudflare free tier** peut être activé devant Traefik pour mettre en cache les vector tiles statiques et les réponses HTTP publiques. Aucun coût. --- ## Architecture applicative ### Backend — Symfony API Symfony n'expose que du JSON (pas de Twig). Deux responsabilités : 1. **API REST** — endpoints consommés par le frontend React 2. **Pipeline** — crons + workers de traitement satellite ### Frontend — React SPA Application React statique servie par FrankenPHP (ou Traefik directement). Consomme l'API Symfony et affiche les vector tiles via MapLibre GL JS. --- ## API Endpoints ### Spots & scores ``` GET /api/spots Liste des CoastalPoints GET /api/spots/{id} Détail d'un CoastalPoint GET /api/spots/{id}/score ImpactScore courant + horizons GET /api/spots/{id}/score?at={datetime} Score à un instant donné ``` ### Observations & prédictions ``` GET /api/observations?bbox={bbox}&date={date} Observations dans une zone GET /api/observations/{id} Détail observation GET /api/forecasts?observationId={id} Prédictions d'une observation GET /api/forecasts?bbox={bbox}&horizon={h} Prédictions par zone + horizon ``` ### Feedback ``` POST /api/feedback Soumettre un UserFeedback anonyme ``` ### Tiles (statiques, hors API) ``` GET /tiles/observations/{z}/{x}/{y}.pbf GET /tiles/forecasts/{horizon}/{z}/{x}/{y}.pbf ``` --- ## Vector Tiles ### Stratégie : Tippecanoe + tiles statiques - Les observations et prédictions sont converties en tiles `.pbf` après chaque ingestion (via Tippecanoe) - Les tiles sont stockées sur le filesystem du VPS et servies statiquement - Pas de rendu dynamique à la volée (Tegola écarté — complexité non justifiée au MVP) - Cloudflare free tier peut mettre ces tiles en cache si activé ### Interdits - GeoJSON brut exposé en production sur des géométries complexes --- ## Caching | Données | Stratégie | TTL suggéré | |---|---|---| | Scores par spot | Redis | 3h (durée du cycle d'ingestion) | | Résultats API observations | Redis | 3h | | Vector tiles statiques | HTTP Cache + Cloudflare | Long (invalidation à chaque ingestion) | | Endpoints publics | HTTP Cache | 15–30 min | Règle : **1 requête = 1 zone + 1 timestamp**, pas de recalcul à la volée. --- ## Sécurité - Rate limiting sur tous les endpoints publics (Symfony RateLimiter) - Aucune donnée personnelle collectée (UserFeedback = fingerprint anonyme) - Retry automatique pipeline avec backoff - Logs structurés sur chaque DataIngestionJob - Secrets (clés API Sentinel Hub, NOAA) via variables d'environnement Docker --- ## Schéma de flux de données ``` Sentinel Hub API └── Cron Symfony (3h) └── DataIngestionJob ├── Calcul AFAI → SargassumObservation │ └── Tippecanoe → tiles statiques /tiles/observations/ └── Simulation dérive → SargassumForecast (×4 horizons) ├── Calcul ImpactScore par CoastalPoint → Redis └── Tippecanoe → tiles statiques /tiles/forecasts/ NOAA GRIB API └── (consommé pendant la simulation de dérive) React SPA ├── MapLibre GL JS → /tiles/...pbf └── Symfony API → /api/spots/{id}/score ```