Architecture - Urbafix Platform¶
Date: 2026-01-04 Version: 2.0 (Stateless-Ready Refactoring) Status: Production
Vue d'ensemble¶
Urbafix est une plateforme de signalement d'incidents urbains permettant aux citoyens de reporter des problèmes (nids-de-poule, éclairage défaillant, déchets, etc.) directement à leur municipalité.
L'architecture a été refactorisée en janvier 2026 pour supporter le scaling horizontal tout en préservant 100% de compatibilité avec le déploiement actuel.
Principes Architecturaux¶
1. Stateless-Ready¶
Les instances web ne stockent aucun état local (sessions, cache, uploads optionnellement externalisés).
2. Opt-In Scalability¶
Activation progressive via variables d'environnement : - Défaut : Single instance, filesystem sessions, no cache - Production : Multi-instances, Redis sessions/cache, S3 uploads
3. Multi-Tenant Strict¶
Isolation par mairie_id à tous les niveaux (SQL, services, API).
4. ANSSI-Compliant¶
- TLS 1.3
- Permissions 0755/0644
- Session regeneration
- Prepared statements
- Face blurring RGPD
5. Separation of Concerns¶
Stack Technique¶
| Composant | Technologie | Version |
|---|---|---|
| Runtime | PHP | 8.2 |
| Web Server | Nginx | 1.24 |
| Database | MariaDB | 10.11 |
| Cache/Sessions | Redis (optionnel) | 7 Alpine |
| Storage | Filesystem / S3 | - |
| Container | Docker | 24+ |
| Orchestration | Docker Compose | 2.x |
Architecture en Couches¶
Modèle de Données¶
Schéma Relationnel¶
Périmètre
Ce diagramme couvre le cœur historique du schéma. Voir Modèle relationnel pour le schéma complet incluant les extensions agents/backoffice (affectation multiple, ATTENTE, carnet de contacts, suivi de position, WebAuthn).
Tables Clés¶
mairies
- Représente chaque municipalité
- Isolation multi-tenant par id
- Lien : code_postal (API publique)
incidents
- Signalements citoyens
- statut : nouveau, en_cours, resolu, rejete
- group_id : Regroupement automatique (pHash + GPS)
- ON DELETE SET NULL pour citoyen_id (préservation stats)
photos_incident
- Photos liées aux incidents
- phash : Perceptual hash (regroupement similaires)
- face_blurred : Floutage RGPD appliqué (0/1)
- faces_count : Nombre de visages détectés
videos_incident - Vidéos liées aux incidents - GPS validation (distance vs incident) - Compression MP4 côté mobile
types_incident - Catégories (nid-de-poule, éclairage, etc.) - Liées à une mairie - Association N-M avec services
services - Départements municipaux (voirie, espaces verts...) - Permissions basées sur types gérés
users
- Utilisateurs admin municipaux
- voir_tous_incidents : Flag admin (voit tout)
- Sinon : filtre par service_id → service_types
Flux de Données Principaux¶
1. Création Incident (API Mobile)¶
Comportement par défaut (QUEUE_DRIVER=sync) : - Jobs exécutés immédiatement (synchrone) - Comportement identique à avant refactorisation
Comportement scalable (QUEUE_DRIVER=redis) : - Jobs poussés dans Redis queue - Workers Supervisord consomment jobs - Traitement asynchrone (face blur, pHash, emails)
2. Consultation Incidents (Admin Backend)¶
3. Regroupement Automatique¶
Avantages : - Évite doublons signalements - Dashboard admin : vue groupée - Statistiques agrégées
Abstractions & Interfaces¶
1. SessionInterface¶
Implémentations :
- FileSessionHandler (default) : $_SESSION natif PHP
- RedisSessionHandler : Sessions Redis centralisées
Factory : SessionFactory::create() selon SESSION_DRIVER
Activation Redis :
Bénéfices : - Multi-instances : sessions partagées - Pas de sticky sessions nécessaire - Failover : Redis AOF persistence
2. StorageInterface¶
Implémentations :
- LocalStorage (default) : Filesystem local
- S3Storage : AWS S3 / MinIO
Méthodes :
put(string $path, string $data): bool
get(string $path): string
exists(string $path): bool
delete(string $path): bool
url(string $path): string
size(string $path): int
Activation S3 :
Bénéfices : - Scalabilité : pas de volumes Docker partagés - Backup : versioning S3 - CDN : CloudFront devant S3
3. CacheInterface¶
Implémentations :
- NullCache (default) : No-op (callback direct)
- RedisCache : Cache centralisé
Pattern Remember :
Activation :
Données cachées : - Types incidents par mairie (TTL 1h) - Mairies par code postal (TTL 1h) - Contacts par type (TTL 1h) - Services et associations (TTL 30min)
Invalidation :
- Automatique sur CREATE/UPDATE/DELETE
- Préfixe clés : urbafix:cache:{mairie_id}:{entity}
4. QueueInterface¶
Implémentations :
- SyncQueue (default) : Exécution immédiate
- RedisQueue : Queue LPUSH/RPOP
Jobs :
- BlurFacesJob : Floutage visages RGPD
- CalculatePhashJob : Calcul pHash photos
- SendEmailNotificationJob : Notifications contacts
Activation :
Workers : bin/worker.php (3 processes Supervisord)
Sécurité¶
Multi-Tenant Isolation¶
Principe : Aucune donnée ne doit fuiter entre mairies.
Implémentation :
1. SQL : Toujours filtrer par mairie_id
2. API : Validation code_postal → mairie_id
3. Services : Permission checks via service_types
4. Cache : Préfixe clés par mairie_id
Audit : /docs/SECURITY_AUDIT_MULTI_TENANT.md
RGPD - Face Blurring¶
Pipeline :
Photo Upload
→ Storage::put()
→ INSERT photos_incident (face_blurred=0)
→ Queue::push('BlurFacesJob')
→ FaceBlurringService::detectFaces()
├─ Extension facedetect (PHP)
└─ Ou Haar Cascade XML
→ FaceBlurringService::applyBlur()
├─ Pixellisation 10x
└─ Gaussian blur 3 passes
→ Fichier écrasé (pas de conservation original)
→ UPDATE face_blurred=1, faces_count=N
Conformité :
- Anonymisation automatique biométrie
- Pas de stockage visages originaux
- Traçabilité BDD (face_blurred, faces_count)
ANSSI Compliance¶
| Règle ANSSI | Implémentation |
|---|---|
| TLS 1.3+ | Reverse proxy (au choix) |
| Permissions fichiers | 0755 dirs, 0644 files |
| Session HttpOnly | SessionHandler flags |
| Session SameSite Strict | Cookie params |
| Prepared statements | Database::execute($sql, $params) |
| Password hashing | password_hash() bcrypt |
| API rate limiting | TODO: middleware |
| Logs structurés | error_log JSON format |
Performance & Scalabilité¶
Optimisations N+1¶
Avant (get_types.php) :
$types = $db->fetchAll("SELECT * FROM types WHERE mairie_id = ?");
foreach ($types as &$type) {
$contacts = $db->fetchAll("SELECT * FROM contacts WHERE type_id = ?", [$type['id']]);
$type['contacts'] = $contacts;
}
Après :
$types = $db->fetchAll("
SELECT t.*, c.email as contact_email, c.nom as contact_nom
FROM types t
LEFT JOIN type_contacts tc ON t.id = tc.type_id
LEFT JOIN contacts c ON tc.contact_id = c.id
WHERE t.mairie_id = ?
");
// Grouper contacts par type_id en PHP
Gain : 1 requête au lieu de N+1
Caching Strategy¶
TTL par type : - Types incidents : 3600s (1h) - Mairies : 3600s (1h) - Services : 1800s (30min) - Contacts : 3600s (1h)
Éviction : allkeys-lru (Redis)
Sizing : 256 MB Redis (suffisant pour 10k incidents)
Horizontal Scaling¶
Configuration :
Load Balancer (au choix)
│
├─ Web Instance 1 (port 8080)
├─ Web Instance 2 (port 8081)
└─ Web Instance 3 (port 8082)
│
└─ Shared Redis (sessions + cache + queue)
└─ Shared MariaDB
└─ Shared S3 (uploads)
Health Check : GET /health.php
- Load balancer retire instances unhealthy
Session Affinity : NON requis (Redis sessions)
Monitoring¶
Health Endpoint¶
URL : GET /health.php
Checks : - Database connection - Redis connection (si activé) - Storage writable - Config valide - PHP extensions
Métriques : - PHP version - Memory usage - Uptime
Intégration : - Docker healthcheck - Load balancer backend checks - Prometheus scraping (TODO)
Logs¶
Format actuel : error_log() plain text
Format futur : JSON structuré
{
"timestamp": 1704398400,
"level": "INFO",
"message": "Incident créé",
"context": {
"incident_id": 123,
"mairie_id": 1,
"user_id": 45,
"ip": "192.168.1.1"
}
}
Agrégation : ELK Stack / Datadog / CloudWatch
Déploiement¶
Environnements¶
| Env | Config | Instances | Redis | S3 |
|---|---|---|---|---|
| Dev | .env local |
1 | Non | Non |
| Staging | .env.staging |
2 | Oui | Non |
| Production | .env.production |
3+ | Oui | Oui |
Blue-Green Deployment¶
# Lancer nouvelle version (green)
docker-compose -f docker-compose.green.yml up -d
# Tester health
curl https://green.urbafix.fr/health.php
# Basculer trafic (reconfiguration du load balancer)
# Arrêter ancienne version (blue)
docker-compose -f docker-compose.blue.yml down
Diagrammes d'Architecture¶
Séquence : Création Incident¶
Roadmap Technique¶
Q1 2026 ✅¶
- Config centralisée
- Session/Storage/Cache abstractions
- Services métier
- Queue async
- Face blurring RGPD
- Multi-tenant audit
- Health endpoint
- Documentation
Q2 2026¶
- Prometheus metrics endpoint
- Workers Redis queue (Supervisord)
- Tests automatisés PHPUnit
- Migration S3 progressive
Q3 2026¶
- Kubernetes Helm charts
- HPA auto-scaling
- Distributed tracing Jaeger
- Read replicas MariaDB
Q4 2026¶
- GraphQL API
- Event sourcing (CQRS)
- ML regroupement incidents
- Mobile app offline-first
Décisions Architecturales (ADR)¶
ADR-001: Abstractions opt-in vs refonte complète¶
Date: 2026-01-01 Statut: Accepté
Contexte: Application monolithique fonctionnelle en production.
Décision: Créer abstractions avec implémentations par défaut identiques au comportement actuel.
Raisons: - Risque zéro régression - Migration progressive possible - Pas d'interruption service
Conséquences: - ✅ Backward compatible 100% - ✅ Production peut rester en mode sync - ⚠️ Code plus verbeux (factories)
ADR-002: Redis optionnel via Docker profiles¶
Date: 2026-01-02 Statut: Accepté
Contexte: Redis pas nécessaire pour déploiements single-instance.
Décision: --profile redis pour activation opt-in.
Raisons: - Pas de dépendance forcée - Réduction coûts infra (petites communes) - Simplicité déploiement
Conséquences: - ✅ Flexibilité déploiement - ⚠️ Documentation claire nécessaire
ADR-003: Queue sync par défaut¶
Date: 2026-01-03 Statut: Accepté
Contexte: Jobs async (pHash, emails) ajoutés.
Décision: QUEUE_DRIVER=sync exécution immédiate par défaut.
Raisons: - Comportement identique actuel - Pas de workers à gérer (simplicité) - Activation async si besoin performance
Conséquences: - ✅ Aucun changement perceptible - ⚠️ Latence upload si nombreux visages
Références¶
- CLAUDE.md - Instructions Claude Code
- CONFIGURATION.md - Variables environnement
- SCALABILITY.md - Scénarios scaling
- DEPLOYMENT.md - Procédures déploiement
- SECURITY_AUDIT_MULTI_TENANT.md - Audit sécurité
- DOCKER_STATELESS.md - Configuration Docker
- FACE_BLURRING.md - Face blurring RGPD