
À propos du projet
Une application de rencontre centrée sur les valeurs, où les photos restent masquées et se dévoilent case par case à mesure que deux personnes échangent vraiment, en React Native sur un backend de recherche vectorielle.
Mon rôle: Conception produit et technique, puis développement full stack de l'application mobile, de l'API et du modèle de données de matching.
J'ai conçu Soulsinq autour d'une inversion assumée du fonctionnement des applications de rencontre. Partout ailleurs la photo est la porte d'entrée et le reste vient après, ce qui entraîne les gens à juger en un quart de seconde. Ici la photo démarre entièrement masquée derrière une grille de vingt cinq cases, et les cases ne se lèvent qu'au fil des échanges réels entre deux personnes en correspondance. L'attirance n'est pas retirée de l'équation, elle est simplement déplacée après la conversation plutôt qu'avant.
Ce parti pris dicte le modèle de données. Le matching ne pouvant pas s'appuyer sur les images, il repose sur les valeurs, les préférences déclarées et la proximité. J'ai utilisé PostgreSQL avec deux extensions qui font le gros du travail : PostGIS pour le filtrage géographique et pgvector pour la recherche par similarité sur les profils de valeurs, toutes deux accédées via Drizzle avec des schémas typés. Un paquet partagé contient les schémas zod et les constantes utilisés par le client comme par le serveur, si bien qu'un changement de forme de profil casse la compilation des deux côtés plutôt que d'apparaître en production comme un bug d'exécution.
L'application mobile est en React Native sur Expo avec expo-router, et l'API est un Express exécuté sur Firebase Functions deuxième génération. Garder l'API sous forme d'application Express classique plutôt que de handlers isolés m'a permis de la lancer en local contre un Postgres Docker et de la tester via les émulateurs Firebase, ce qui a maintenu une boucle de retour courte pendant que le design produit bougeait encore.
Fonctionnalités principales
- 🧩 Révélation progressive des photos : La photo de profil se cache derrière vingt cinq cases qui ne se lèvent qu'au fil des échanges réels.
- 🎯 Matching fondé sur les valeurs : La compatibilité se calcule à partir des valeurs et préférences déclarées plutôt que de l'apparence.
- 🧠 Recherche par similarité vectorielle : pgvector compare les profils de valeurs dans PostgreSQL, si bien que le matching reste une requête base et non un service à part.
- 📍 Filtrage géographique : PostGIS restreint les candidats par distance réelle avant tout calcul de similarité, ce qui garde les requêtes rapides à mesure que la base grandit.
- 📱 Un seul code pour iOS et Android : React Native sur Expo avec expo-router livre les deux plateformes depuis un unique arbre de navigation.
- 🔗 Contrat partagé entre app et API : Les schémas zod vivent dans un paquet partagé, si bien qu'un changement de profil casse la compilation plutôt que la production.
- 🧪 Développement entièrement local : Un Postgres Docker avec PostGIS et pgvector, plus les émulateurs Firebase, reproduisent toute la stack hors ligne.
- ⚙️ Express sur Cloud Functions : Conserver une application Express classique plutôt que des handlers isolés rend l'API testable de bout en bout dans un seul processus.
Problèmes et solutions
Problème: Les applications de rencontre placent la photo en premier, ce qui entraîne à décider en une fraction de seconde et enterre tout ce qu'un profil raconte réellement. Masquer simplement les photos ne marche pas non plus, car l'utilisateur abandonne une application qui semble lui cacher quelque chose.
Solution: La photo est présente dès le départ mais couverte d'une grille de vingt cinq cases qui se lèvent au fil de la conversation. Rien n'est caché, la révélation se mérite, et le matching s'appuie pendant ce temps sur des valeurs comparées par similarité vectorielle plutôt que sur l'apparence.

