Initial commit : docs (specs, data-model, architecture, roadmap)

This commit is contained in:
Gwadaking
2026-03-31 23:24:51 -04:00
commit e44916d444
8 changed files with 1247 additions and 0 deletions

150
docs/architecture.md Normal file
View File

@@ -0,0 +1,150 @@
# 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
```