Files
radarsargasses/docs/architecture.md

151 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
```