Le Contexte et le Problème
Chercher un emploi impose souvent de jongler entre LinkedIn, Indeed, France Travail et une dizaine d'autres plateformes — chacune avec son interface, ses filtres, son algorithme. DevSpot part d'un constat simple : centraliser les offres dans une seule interface, avec une recherche géolocalisée efficace et un suivi structuré des candidatures.
Le projet couvre les deux faces du problème : l'agrégation et la normalisation des données côté API, et l'expérience de recherche + suivi côté frontend.
Architecture et Choix Techniques
Le backend repose sur Laravel 12 avec une architecture Clean/Hexagonale stricte. Le code est organisé en trois couches isolées : **Domain** (entités, ports, value objects), **Application** (use cases), **Infrastructure** (adapters, repositories Eloquent, adaptateurs source). Aucune dépendance ne traverse ces frontières sans passer par un port — ce qui rend chaque source d'offres d'emploi remplaçable indépendamment.
Le frontend Next.js 16 consomme l'API via SWR. Les filtres (titre, localisation, rayon) vivent dans les URL search params — pas de store global — ce qui rend chaque recherche partageable par URL et compatible avec la navigation navigateur.
L'authentification est gérée par Laravel Sanctum avec Laravel Socialite pour les flux OAuth Google et GitHub. Redis assure la gestion des sessions, du cache et de la file d'attente.
Défis Techniques et Solutions
Le premier chantier a été la normalisation des sources. Adzuna et France Travail exposent des formats JSON radicalement différents — structure, conventions de nommage, format des salaires, encodage de la localisation. Chaque source dispose de son propre adapter dans la couche Infrastructure, qui mappe vers une entité JobOffer commune du Domain. Un hash sur titre + entreprise + localisation évite les doublons entre sources.
Le Kanban de suivi des candidatures a nécessité une gestion fine du drag & drop avec dnd-kit. Les colonnes représentent les statuts (à envoyer, envoyée, entretien, refus), et les cartes sont déplacées avec une mise à jour optimiste côté client avant confirmation API — pour que l'UI reste réactive même sur connexion lente.
- —Géolocalisation via l'API
navigator.geolocationdu navigateur, coordonnées transmises à l'API pour une recherche par rayon - —Pagination URL-driven :
?page=2relance automatiquement le fetch SWR sans re-monter le composant - —Déduplication backend par hash pour éviter les offres dupliquées entre Adzuna et France Travail
Conclusion et Apprentissages
DevSpot m'a permis d'appliquer concrètement une architecture hexagonale sur un projet Laravel — pas comme exercice académique, mais sous la contrainte réelle d'intégrer deux APIs tierces aux formats incompatibles. La valeur des ports se révèle au moment d'ajouter une troisième source : l'adapter s'ajoute sans toucher au Domain ni à l'Application.
Côté frontend, le modèle URL-first pour les filtres s'est avéré bien plus solide qu'un store : aucune désynchronisation entre l'URL et l'état UI, et le partage d'une recherche se résume à copier l'URL.





