Le contexte
Dorloter est une plateforme française dédiée aux animaux domestiques, qui réunit trois services habituellement dispersés : l'adoption en refuge, le signalement d'animaux perdus ou trouvés, et l'annuaire des pensions professionnelles agréées. Le MVP cible chats et chiens, avec une conception extensible aux NAC sans refonte.
Elle s'adresse à quatre types d'acteurs, adoptants, refuges, pensions et administrateurs, servis par trois clients distincts : une vitrine publique, un back-office professionnel et une application mobile. Je l'ai conçue et développée seul, en quatre mois. Réunir trois métiers dans un seul produit est moins une somme de fonctionnalités qu'un problème de frontières, et trois contraintes en découlent.
Trois métiers, un seul produit. L'adoption, les retrouvailles et les pensions n'ont ni les mêmes acteurs, ni les mêmes règles, ni le même rythme d'évolution, mais elles partagent des utilisateurs, une base de données et une API. Chaque domaine doit pouvoir évoluer sans entraîner les autres.
Un rapprochement qui doit tomber juste. Rapprocher un animal perdu d'un animal trouvé croise proximité, morphologie et date. Un tri fait par l'application, après avoir remonté les candidats, ne tient pas la charge.
Un bénévole n'est pas un rôle global. Les droits dans un refuge dépendent de l'équipe à laquelle on appartient, pas du rôle porté par le jeton d'authentification. Confondre les deux mène à une hiérarchie de rôles qui finit toujours par craquer.
La chaîne des retrouvailles
C'est la fonction qui justifie le choix de la pile de données : tout le reste en découle.
particulier
Signalement
Déclaration d'un animal perdu ou trouvé, photos à l'appui.
plateforme
Géolocalisation
Le point est enregistré comme donnée géographique, pas comme deux nombres.
base
Recherche de proximité
La base restreint elle-même les candidats du type opposé à un périmètre.
plateforme
Scoring
Plusieurs critères pondérés donnent une note unique à chaque candidat.
plateforme
Mise en relation
Au-delà du seuil, la correspondance est proposée aux deux déclarants.
Un signalement reste actif : chaque nouvelle déclaration est confrontée à celles déjà en base, dans les deux sens. Un animal trouvé aujourd'hui peut correspondre à une perte signalée la semaine dernière.
Trois conséquences ont orienté l'architecture. La position est stockée comme une géométrie et non comme un couple de coordonnées, ce qui permet à la base de faire le travail. Le rapprochement est une recherche continue, pas une requête ponctuelle : la fraîcheur des données compte autant que la justesse du calcul. Et le barème est isolé dans une fonction pure, sans accès à la base, ce qui le rend vérifiable sans aucune infrastructure. Cette dernière frontière est celle qui rapporte le plus, et j'y reviens plus bas.
Les choix techniques
API
NestJS, monolithe modulaire
Des modules métier dans un seul déploiement, où chacun n'accède aux autres que par leurs services exportés. Les frontières des microservices sans leur exploitation, pour une équipe d'une personne.
Accès aux données
Kysely, sans ORM
Le typage des requêtes est vérifié à la compilation, mais rien ne s'interpose entre le code et le SQL. Le schéma est décrit en TypeScript et les migrations restent des fichiers versionnés, appliqués au démarrage.
Recherche géospatiale
Déléguée à la base
Proximité et distance sont calculées là où sont les données, sur une colonne géographique indexée, au lieu de remonter les candidats pour les filtrer en mémoire.
Autorisation refuge
Permissions dérivées de l'équipe
Des permissions fines dérivées de rôles d'équipe. Un bénévole invité garde le rôle global le plus faible : ses droits viennent de son appartenance, pas de son jeton.
Deux décisions de contrat complètent ces choix. Les réponses de l'API suivent des enveloppes figées, avec des codes d'erreur stables et une pagination par curseur : ce contrat n'a pas bougé depuis l'origine, donc les trois clients restent alignés sans régénération. Et la plateforme n'embarque aucun cookie non essentiel, aucune ressource servie par un tiers, aucun traceur, ce qui l'exempte de bandeau de consentement et rend la politique publiée conforme à ce que le code fait réellement.
L'architecture qui en découle
Deux packages partagés tiennent les clients alignés sans les fusionner : un design system commun aux deux applications web, et une couche d'accès à l'API qui porte les jetons et leur rotation. La vitrine et l'espace pro ont des publics et des besoins d'ergonomie opposés, ils restent donc deux applications sur deux domaines, et l'API demeure la seule frontière de sécurité.
Ce que la chaîne de tests couvre
La suite est volontairement étroite. Elle ne cherche pas un taux de couverture, elle vise les endroits où une erreur ne se voit pas à la relecture et ne casse aucun parcours : un barème qui dérive d'un point, une table oubliée dans un effacement, un hachage qui accepte ce qu'il devrait refuser.
Complétude de l'effacement RGPD
Le vrai risque n'est pas que la suppression de compte cesse de marcher, c'est qu'une migration ajoute une table rattachée à un utilisateur et que le chemin d'effacement l'oublie. Le test relève les clés étrangères obligatoires vers la table des comptes, les confronte à ce que le service purge réellement, et échoue sur tout écart. Une table nouvellement rattachée doit être purgée ou déclarée conservée avec sa justification légale. La vérification est statique : elle tourne sans base de données.
Barème de rapprochement
Le barème étant une fonction pure, chaque critère est vérifié isolément sur ses paliers, puis leur somme, puis le cas où tout diverge et où le total doit passer sous le seuil de mise en relation. Aucune base, aucune fixture, donc aucune raison de ne pas rejouer ces cas à chaque exécution.
Hachage des mots de passe
Vecteurs de référence de l'implémentation d'origine, normalisation Unicode du mot de passe avant hachage, aller-retour encodage puis vérification, et rejet d'un hash malformé sans lever d'exception. Ce dernier point compte : une erreur non rattrapée à la vérification transforme une donnée corrompue en panne.
Routage des notifications mobiles
La traduction d'une notification en destination dans l'application, sur des charges utiles hostiles : absentes, sans identifiant exploitable, portant deux identifiants concurrents, ou avec des types inattendus. Une notification qui ouvre le mauvais écran est un bug qu'aucun test de rendu ne rattrape.
Vingt et un cas, cinquante-deux assertions, quatre fichiers, moins d'une seconde d'exécution. C'est peu, et c'est assumé : sur un MVP mené seul, une suite courte qui tourne à chaque commit vaut mieux qu'une suite large qu'on finit par désactiver. Le test d'effacement est celui que je referais en premier sur n'importe quel produit manipulant des comptes.
Le back-office qui va avec
Un refuge ne se gère pas avec une liste d'annonces. La console pro couvre le registre légal d'entrée et de sortie, les contrats d'adoption, les familles d'accueil, le suivi médical, les bénévoles, les événements, le stock et les campagnes de communication. C'est la partie la moins visible du produit et la plus volumineuse.
L'accès aux pensions, lui, passe par une vérification humaine : numéro d'entreprise et agrément préfecture sont contrôlés avant publication. C'est le seul point de la plateforme où un humain bloque volontairement le flux, parce qu'un annuaire de professionnels agréés qui ne vérifie pas les agréments ne vaut rien.
Où en est le produit
Le périmètre MVP est livré : une API de cent quarante routes, trois clients qui consomment le même contrat, un rapprochement géospatial délégué à la base, et la conformité RGPD écrite dans le code plutôt que dans une page. Restent des chantiers identifiés, dont les notifications navigateur et une description d'API exhaustive. La conception vise les chats et les chiens sans fermer la porte aux NAC : l'extension est une affaire de données, pas de refonte.


