WonderWork¶
WonderWork est une plateforme SaaS B2B RH qui met en relation des candidats (chercheurs d'emploi) et des recruteurs (entreprises qui recrutent) via un algorithme de compatibilité.
Le problème qu'elle résout : le matching RH traditionnel repose sur des CVs libres et des mots-clés. Les candidats envoient des candidatures non ciblées, les recruteurs trient des centaines de profils manuellement. WonderWork structure les deux côtés — un passport candidat normalisé, des offres avec critères précis — pour calculer un score de compatibilité objectif et présenter à chaque candidat uniquement les offres qui lui correspondent vraiment.
Acteurs¶
| Acteur | App | Rôle |
|---|---|---|
| Candidate | candidate/ — PWA responsive |
Complète son Employment Passport, consulte les offres classées par score de compatibilité, suit ses candidatures |
| Recruiter | recruiter/ — Web app desktop |
Publie des offres, gère les candidatures via un pipeline ATS Kanban, headhunte des candidats |
| Admin | admin/ — Back-office interne |
Gestion des comptes entreprises, supervision de la plateforme (WonderWork team uniquement) |
Concepts clés¶
- Employment Passport
- Profil structuré du candidat — remplace le CV. Collecté via un questionnaire guidé en onboarding. Contient : expériences professionnelles avec codes ROME, compétences du catalogue WonderWork, valeurs culturelles recherchées, contraintes logistiques (salaire minimum, mobilité, disponibilité). Sert de base au calcul du score de matching.
- Matching Score
- Score de compatibilité 0–100 calculé entre un passport et une offre d'emploi. Décomposé en 6 blocs pondérés indépendants. Pré-calculé et stocké en base — jamais calculé à la demande pour les performances.
| Bloc | Critère | Poids | Ce qu'il mesure |
|---|---|---|---|
| BLOC 1 | Intitulé de poste (ROME) | 20 % | Alignement du métier cible du candidat avec le code ROME de l'offre |
| BLOC 2 | Compétences techniques | 41 % | Recouvrement compétences/langages candidat ↔ compétences requises de l'offre |
| BLOC 3 | Expérience & séniorité | 17 % | Années d'expérience calculées vs niveau requis par l'offre |
| BLOC 4 | Valeurs culturelles | 10 % | Intersection valeurs candidat ↔ valeurs de l'entreprise |
| BLOC 5 | Logistique | 7 % | Salaire, lieu, mode de travail, disponibilité |
| BLOC 6 | Langues | 5 % | Langues maîtrisées vs langues requises de l'offre |
- Multi-tenant
- Chaque
Companypossède un ou plusieurs comptesRecruiter. Les données sont strictement isolées parcompany_id— un recruteur ne voit que les offres et candidatures de son entreprise. Les comptes entreprise sont créés par l'équipe WonderWork — pas d'auto-inscription publique. - Référentiel ROME
- WonderWork utilise le référentiel ROME 4.0 (France Travail) comme vocabulaire contrôlé pour les métiers. Chaque expérience professionnelle et chaque offre d'emploi est associée à un code ROME — ce qui rend le matching sémantique possible sans dépendre du libellé libre du titre de poste.
Comment ça marche — flux principal¶
sequenceDiagram
participant C as Candidat
participant API as API Symfony
participant W as Worker async
participant R as Recruteur
C->>API: Complète son Employment Passport
API->>W: Message Symfony Messenger
W->>API: Calcule scores matching (6 blocs)
Note over W,API: Pré-calcul stocké en match_scores
C->>API: GET /v1/me/matches
API-->>C: Offres triées par score desc
C->>API: Postule à une offre
API-->>R: Notification nouvelle candidature
R->>API: Déplace la candidature dans le pipeline
API-->>C: Notification changement de statut
Le calcul du score n'est jamais fait à la demande — il est déclenché en arrière-plan (Symfony Messenger) à chaque modification du passport ou publication d'une offre. La liste des matches affichée au candidat est une lecture directe de la table match_scores.
Structure du monorepo¶
wonderwork/
├── api/ ← Symfony 7.4 + API Platform 4.x (REST API, logique métier, async)
├── candidate/ ← Next.js 15 PWA (app candidat, responsive)
├── recruiter/ ← Next.js 15 (app recruteur, desktop-first)
├── admin/ ← Next.js 15 (back-office interne)
├── infra/
│ ├── ansible/ ← Déploiement applicatif (Docker Swarm + Nginx + TLS)
│ └── terraform/← Provisionnement infrastructure (OVH DNS + firewall)
├── docs/ ← Cette documentation
├── CLAUDE.md ← Contexte IA
└── DESIGN.md ← Design system
Tout le code vit dans un seul dépôt Git. Chaque app a son propre pipeline CI avec des path filters — un push dans candidate/ ne déclenche pas le build de l'API.
Modèle de déploiement¶
flowchart LR
Browser["Navigateur / Mobile"] --> Nginx
subgraph VPS["VPS OVH (staging.wonderwork.fr)"]
Nginx["Nginx\n(reverse proxy + TLS)"]
Nginx --> Candidate["candidate:3000"]
Nginx --> Recruiter["recruiter:3001"]
Nginx --> API["api:8000"]
API --> DB[(PostgreSQL)]
API --> Redis[(Redis)]
end
API -->|SMTP| Brevo["Brevo\n(emails transactionnels)"]
GHCR["GHCR\n(ghcr.io)"] -->|pull images| VPS
Tous les services tournent dans des conteneurs Docker sur un unique VPS OVH. Nginx est le seul point d'entrée public — il route le trafic vers les bons conteneurs selon le nom de domaine et gère la terminaison TLS.
Contraintes de souveraineté¶
Toute l'infrastructure de production est hébergée sur des serveurs français (OVH). Aucun service US en production — exigence légale liée au CLOUD Act américain et différenciateur commercial pour les clients entreprise et secteur public français.
| Service | Hébergement |
|---|---|
| VPS + base de données | OVH (Roubaix, FR) |
| Emails transactionnels | Brevo (FR, serveurs EU) |
| Registre de conteneurs | GHCR (GitHub — acceptable pour les images binaires, pas pour les données personnelles) |