Géocodage, documents et import de données : les services transversaux de l’API

Cet article fait partie d’une série consacrée à l’API REST de Rezo Pouce. Après avoir présenté l’architecture générale, l’authentification et le service Rezo Séniors, ce dernier volet aborde les services transversaux qui irriguent l’ensemble de l’API : le géocodage, la gestion documentaire, la génération de PDF, l’import de données et la synchronisation avec Mobicoop.

Le GeocodeBundle : une architecture hexagonale pour le géocodage

Le géocodage — transformer une adresse en coordonnées géographiques et inversement — est omniprésent dans Rezo Pouce. Chaque point d’arrêt, chaque adresse de trajet, chaque commune doit être localisée. L’article consacré à la cartographie du back-office présentait ce mécanisme du point de vue du front. Cet article s’intéresse à ce qui se passe côté serveur.

Le problème des fournisseurs

Aucun service de géocodage n’est fiable à 100 %. La Base Adresse Nationale (BAN), interrogée via Addok, est gratuite et excellente sur le territoire français, mais peut ne pas reconnaître certains lieux-dits. Pelias, basé sur OpenStreetMap, offre une couverture mondiale mais avec une précision variable. Le géocodeur Mobicoop apporte une source supplémentaire pour l’autocomplétion.

La solution retenue est un système de fournisseurs interchangeables avec stratégie de fallback. Le GeocodeManager orchestre les appels : chaque requête est envoyée au fournisseur par défaut, et en cas d’échec ou de résultat insuffisant, les sources secondaires prennent le relais. Aujourd’hui, c’est le géocodeur Mobicoop — auto-hébergé par la coopérative — qui implémente ce rôle de fournisseur principal, en encapsulant les appels aux différentes sources derrière un adaptateur unique.

L’architecture hexagonale

Le calcul d’itinéraire illustre le mieux cette approche. Le code est découpé en couches strictement séparées :

  • Présentation — Le GeorouteController reçoit la requête HTTP, valide les paramètres (au moins deux points, coordonnées dans les plages valides).
  • Cas d’usage — Le GetRouteHandler orchestre l’appel au service de domaine.
  • Domaine — Le GeorouteService implémente la logique métier, indépendamment de tout fournisseur.
  • Port — Le GeorouterPort définit le contrat que tout fournisseur de routage doit respecter.
  • Adaptateur — Le MobicoopGeorouterAdapter implémente ce contrat en appelant le moteur de routage auto-hébergé par Mobicoop.

Cette séparation permet de changer de fournisseur de routage — ce qui est arrivé quand nous avons décidé en équipe de remplacer le service GraphHopper payant par un déploiement auto-hébergé de la même solution — sans toucher au domaine ni à la présentation. Seul l’adaptateur change.

Cette architecture hexagonale n’est pas née dans Rezo Pouce — c’est en travaillant sur la v3 de Mobicoop, une application mobile adossée à une architecture micro-services, que j’ai expérimenté ce design pattern à grande échelle. La lisibilité, la maintenabilité et la testabilité qu’il apporte m’ont convaincu de l’introduire dans Rezo Pouce, en commençant par le GeocodeBundle où le besoin d’interchangeabilité des fournisseurs le justifiait pleinement.

Les convertisseurs

Chaque fournisseur de géocodage retourne ses résultats dans un format propre. Un convertisseur (GeocodingConverter) par fournisseur normalise les réponses vers un format unifié : label, numéro, rue, localité, code postal, code commune, coordonnées. Les consommateurs de l’API — le front comme les autres services internes — ne savent jamais quel fournisseur a effectivement répondu.

L’autocomplétion d’adresses

L’autocomplétion est le point d’entrée le plus fréquent du géocodage. Quand un gestionnaire saisit une adresse dans le back-office — pour créer un point d’arrêt, définir un départ de trajet Séniors ou rechercher une commune — chaque frappe déclenche une requête vers l’endpoint /autocomplete. Le GeocodeManager interroge le fournisseur par défaut (le géocodeur Mobicoop) et valide les résultats contre les communes présentes en base, garantissant que seules des adresses pertinentes pour le réseau Rezo Pouce sont proposées.

L’enrichissement des résultats

Le géocodage ne se limite pas à retourner des coordonnées. Sur chaque résultat, le GeocodeManager enrichit la réponse avec les données Rezo Pouce : la commune est-elle en base ? Est-elle rattachée à un territoire ? Ce territoire est-il actif ? Ces informations sont essentielles pour le back-office, qui doit savoir si un point d’arrêt se situe dans une commune éligible.

Le DocumentBundle : stockage typé et gestion de fichiers

Le DocumentBundle gère l’upload, le stockage et la manipulation de fichiers avec un système de typage rigoureux.

Le typage des documents

Chaque document est rattaché à un type qui détermine :

  • Le chemin de stockage — les photos de points d’arrêt, les scans d’identité et les lettres du président ne sont pas rangés au même endroit.
  • Les formats acceptés — PDF, images, CSV, polices, formats SIG selon le type.
  • Le rôle minimum pour accéder au document — un scan d’identité n’est pas accessible à un consultant.
  • La conversion automatique — certains types déclenchent une conversion en JPG à l’upload.
  • Le redimensionnement — activé par défaut, désactivable par type.

Les fichiers sont nommés par hash MD5 (sans extension dans le nom), ce qui élimine les collisions et les problèmes de caractères spéciaux. Les vignettes de prévisualisation sont générées automatiquement pour les PDF.

Le cycle de vie

Le nettoyage est géré par les lifecycle callbacks de Doctrine : quand un document est supprimé en base, le fichier physique est supprimé automatiquement (PreRemove). Les vignettes associées suivent le même cycle grâce aux cascades de suppression.

La génération de PDF

La génération de PDF intervient à trois niveaux dans l’API. Le processus est toujours le même : des données extraites, un template HTML/CSS rendu par Twig, et une conversion en PDF via wkhtmltopdf. Ce qui change, c’est la nature du document. La contrepartie de cette approche est que wkhtmltopdf repose sur un moteur WebKit ancien qui gère très mal les mises en page modernes — ni Flexbox ni CSS Grid ne sont exploitables. Les templates sont donc contraints à des layouts en float et table, ce qui limite la souplesse de la mise en page.

Les fiches destination

La fiche destination est un document imprimable qu’un usager peut présenter au bord de la route pour indiquer son trajet. Elle est générée dynamiquement à partir des informations saisies — destination, commune, point d’arrêt.

Les fiches mobilité

Les fiches mobilité sont des documents multi-pages présentant les points d’arrêt d’une commune. Le configurateur du back-office sauvegarde les paramètres de mise en page en JSON (nombre de points par page, pages personnalisées, captures de carte), et l’API assemble le tout au moment du téléchargement.

Le téléchargement en lot — toutes les fiches d’un territoire dans une archive ZIP — impose une gestion attentive de la mémoire et des temps de réponse. Chaque fiche est générée à la volée, garantissant des données toujours à jour.

Les rapports statistiques

Les bilans de trajets par période suivent le même processus : données extraites, template Twig rendu, conversion en PDF. Ces rapports sont disponibles par territoire, par utilisateur et par période.

L’import de données en masse

L’import CSV de points d’arrêt illustre une problématique classique du développement full-stack : recevoir un fichier depuis le front, le traiter côté serveur et renvoyer un retour exploitable.

La détection de format

Le parser gère d’abord les problèmes les plus fréquents : l’encodage du fichier (UTF-8, Latin-1, Windows-1252) et le séparateur de champs (virgule, point-virgule, tabulation). Ces deux paramètres, anodins en apparence, sont la première source d’échec en conditions réelles — les fichiers proviennent de tableurs variés, avec des configurations locales différentes. Le parser détecte l’encodage et identifie le séparateur avant de commencer le traitement.

La validation adaptative

Vient ensuite la validation : correspondance des en-têtes avec les champs attendus, puis validation ligne par ligne avec typage adaptatif — les booléens acceptent « oui », « vrai », « true » ou « 1 ». Les coordonnées géographiques fournies sont automatiquement géocodées pour obtenir l’adresse postale.

Chaque erreur est collectée dans un rapport détaillé plutôt que de provoquer un arrêt immédiat. Le gestionnaire peut ainsi corriger son fichier en une seule passe, au lieu de découvrir les erreurs une par une.

La synchronisation avec Mobicoop

L’API Rezo Pouce ne vit pas en isolation — elle synchronise ses données avec l’API Mobicoop, la coopérative qui porte aujourd’hui le service. Les territoires, les gestionnaires et les points d’arrêt sont synchronisés de manière événementielle.

Les points d’arrêt

Quand un point d’arrêt est créé ou modifié dans le back-office, un événement Symfony (BreakpointActivationEvent, BreakpointRelaypointUpdatedEvent) déclenche automatiquement sa création ou sa mise à jour côté Mobicoop en tant que point relais. L’entité RelayPoint convertit un point d’arrêt Rezo Pouce en payload Mobicoop — adresse, coordonnées, nom, description, identifiant externe, statut.

L’activation d’une commune déclenche la synchronisation en masse de tous ses points d’arrêt actifs. Le passage d’un territoire au statut « en place » provoque la même synchronisation pour tous ses points. Côté Mobicoop, les points d’arrêt sont convertis en points relais et affichés par des applications spécifiques ; par exemple Mobicoop Pouce.

Les gestionnaires

La synchronisation fonctionne aussi dans l’autre sens : les gestionnaires Mobicoop peuvent être importés automatiquement dans Rezo Pouce. Le service AutocreateUser recherche chaque gestionnaire par email et, s’il n’existe pas, crée automatiquement le compte avec les rôles appropriés.

Le couplage événementiel

Ce couplage par événements — plutôt que par synchronisation périodique — garantit que les deux systèmes restent cohérents en quasi temps réel. Seuls les points d’arrêt appartenant à un territoire public sont synchronisés, évitant de polluer Mobicoop avec des données de test ou des territoires inactifs.

Ce que ces services illustrent

Les services transversaux de l’API — géocodage, documents, PDF, import, synchronisation — ne sont pas les plus visibles, mais ils sont les plus sollicités. Le géocodage intervient à chaque création de point d’arrêt, à chaque saisie d’adresse, à chaque import CSV. La gestion documentaire est consommée par tous les modules. La synchronisation Mobicoop tourne en arrière-plan à chaque modification.

Ce qui les unit, c’est une préoccupation commune : l’abstraction des fournisseurs. Le géocodage abstrait ses sources derrière un port. Les documents abstrait leur stockage derrière un système de types. La synchronisation abstrait Mobicoop derrière des événements Symfony. Chaque fois qu’un composant externe change — un fournisseur de géocodage devient payant, un format de fichier évolue, l’API Mobicoop modifie ses endpoints — l’impact est contenu dans un adaptateur, sans propagation au reste du système.

Une question ou un projet ?

Je suis disponible pour en discuter.

Me contacter