Aller au contenu

Vue d'ensemble - Architecture

⬇️ Télécharger cette page en Markdown


Stack Technique Complet

Backend

Composant Technologie Version Rôle
Runtime PHP 8.2 Logique métier, API REST
Web Server Nginx 1.24 Reverse proxy, SSL termination
Framework - - Architecture custom MVC
Extensions PDO, GD, Imagick, Redis - Database, images, cache

Base de Données

Composant Technologie Version Rôle
SGBD MariaDB 10.11 Persistance données
Engine InnoDB - Transactions ACID
Charset utf8mb4 - Support emojis
Cache Redis 7 Alpine Sessions, cache, queue

Frontend

Composant Technologie Version Rôle
Admin HTML/CSS/JS - Interface backoffice
Cartes OpenStreetMap - Affichage géographique
Mobile Android Kotlin SDK 34 Application citoyenne

Infrastructure

Composant Technologie Version Rôle
Container Docker 24+ Isolation services
Orchestration Docker Compose 2.x Multi-services
Stockage Filesystem / S3 - Photos, vidéos
Monitoring Health endpoint - Load balancer checks

Architecture Globale

Architecture globale de MonQuartier


Composants Principaux

1. API REST Backend

Localisation : /public/backend/api/

Endpoints publics : - get_types.php : Types d'incidents par code postal - get_incidents.php : Liste incidents - submit_incident.php : Création incident + photos/vidéos - get_user_incidents.php : Incidents d'un citoyen - get_incident_details.php : Détail incident

Authentification : - X-Fingerprint : Device ID mobile - X-API-Key : Token optionnel

Format : JSON

2. Backoffice Administration

Localisation : /public/backend/

Pages : - index.php : Dashboard - incidents.php : Gestion incidents - types.php : Configuration types - services.php : Gestion services municipaux - users.php : Gestion utilisateurs - mairie.php : Configuration municipalité

Authentification : Session PHP/Redis

3. Application Mobile Android

Localisation : /android/

Architecture : MVVM (Model-View-ViewModel)

Composants : - Room Database (offline-first) - Retrofit (API calls) - Jetpack Compose (UI) - osmdroid (cartes) - WorkManager (sync background)

Fonctionnalités : - Signalement hors-ligne - Floutage visages ML Kit - Compression photos/vidéos - Synchronisation différée


Flux de Données

Création Incident (Mobile → Backend)

Séquence de création d'un incident depuis l'app mobile

Consultation Dashboard (Admin)

Séquence de consultation de la liste des incidents côté admin


Sécurité Multi-Tenant

Isolation par Mairie

Principe Fondamental

Aucune donnée ne doit fuiter entre municipalités différentes.

Mécanismes :

  1. SQL Filtering :

    SELECT * FROM incidents
    WHERE mairie_id = ?
    AND type_id IN (?)
    

  2. Service-Based Permissions :

    $typeIds = Auth::getAccessibleTypeIds();
    if ($typeIds !== null) {
        $sql .= " AND type_id IN (" . implode(',', $typeIds) . ")";
    }
    

  3. Cache Keys Prefixing :

    $key = "urbafix:cache:{$mairieId}:incidents:{$hash}";
    

  4. API Validation :

    $mairie = MairieService::getByCodePostal($codePostal);
    if (!$mairie) {
        throw new Exception("Mairie non trouvée");
    }
    


Performance

Optimisations Appliquées

N+1 Queries : - ❌ Avant : 1 + N requêtes - ✅ Après : 1 requête avec LEFT JOIN

Caching Strategy : - Types incidents : TTL 3600s - Mairies : TTL 3600s - Services : TTL 1800s

Indexes Database :

CREATE INDEX idx_incidents_mairie ON incidents(mairie_id);
CREATE INDEX idx_incidents_type ON incidents(type_id);
CREATE INDEX idx_incidents_statut ON incidents(statut);
CREATE INDEX idx_phash ON photos_incident(phash);
CREATE INDEX idx_gps ON incidents(latitude, longitude);


Scalabilité

Configuration Single Instance (Par défaut)

SESSION_DRIVER=file
CACHE_DRIVER=null
STORAGE_DRIVER=local
QUEUE_DRIVER=sync

Caractéristiques : - 1 container web - Sessions filesystem - Pas de cache - Jobs synchrones

Capacité : ~1000 incidents/mois, ~50 utilisateurs admin

Configuration Multi-Instances (Production)

SESSION_DRIVER=redis
CACHE_DRIVER=redis
STORAGE_DRIVER=s3
QUEUE_DRIVER=redis

Caractéristiques : - 3+ containers web - Load balancer (au choix) - Redis centralisé - S3 pour uploads - Workers Supervisord

Capacité : ~100k incidents/mois, ~500 utilisateurs admin


Monitoring

Health Endpoint

URL : GET /health.php

Checks : - Database connection - Redis connection (si activé) - Storage writable - Config valide - PHP extensions

Réponse :

{
  "status": "healthy",
  "checks": {
    "database": "ok",
    "redis": "ok",
    "storage": "ok"
  },
  "metrics": {
    "php_version": "8.2.0",
    "memory_usage_mb": 45,
    "uptime_seconds": 86400
  }
}

Intégration : - Docker healthcheck - Load balancer backend checks - Prometheus scraping (futur)


Dépendances Externes

Services Cloud (Optionnels)

  • AWS S3 : Stockage uploads (production)
  • SendGrid : Envoi emails SMTP
  • Let's Encrypt : Certificats SSL
  • Datadog : Monitoring APM (futur)

APIs Tierces

  • geo.api.gouv.fr : Géolocalisation inverse (code postal)
  • OpenStreetMap : Tuiles cartographiques
  • ML Kit Face Detection : Floutage visages (mobile)

Points d'Extension

Ajout d'un Nouveau Service Métier

// 1. Créer /src/Services/MonService.php
namespace Urbafix\Services;

class MonService {
    private $db;
    private $cache;

    public function __construct(Database $db, CacheInterface $cache) {
        $this->db = $db;
        $this->cache = $cache;
    }

    public function maMethode() {
        return $this->cache->remember('cle', 3600, function() {
            return $this->db->fetchAll("SELECT ...");
        });
    }
}

// 2. Utiliser dans controller
$service = new MonService(Database::getInstance(), CacheFactory::create());
$data = $service->maMethode();

Ajout d'un Endpoint API

// /public/backend/api/mon_endpoint.php
<?php
require_once __DIR__ . '/../../../src/Config.php';
require_once __DIR__ . '/../../../src/Database.php';
require_once __DIR__ . '/../../../src/Services/MonService.php';

header('Content-Type: application/json');

try {
    $service = new MonService(Database::getInstance(), CacheFactory::create());
    $data = $service->maMethode();

    echo json_encode(['success' => true, 'data' => $data]);
} catch (Exception $e) {
    http_response_code(500);
    echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}

Prochaines Étapes