4.6 KiB
4.6 KiB
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 :
- API REST — endpoints consommés par le frontend React
- 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
.pbfaprè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