Files
radarsargasses/docs/architecture.md

4.6 KiB
Raw Blame History

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 1530 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