đź”§ Architecture Backend CV-builder

Note

📺 Diagrammes SVG de ce document

Documents liĂ©s : Cas d’utilisation · Diagramme de classes

Track Technique 2TUP — Phase 1 : Choix d’architecture

Style d’architecture : monolithe modulaire DDD dĂ©coupĂ© en bounded contexts (cvbuilder, ai, job, user, shared.storage), chaque contexte Ă©tant organisĂ© en couches hexagonales Presentation → Application → Domain (ports) → Infrastructure (adapters). L’API est spec-first OpenAPI : les controllers implĂ©mentent les interfaces gĂ©nĂ©rĂ©es depuis openapi.yaml (CvsApi, AiApi, CvMatchingApi, FilesApi).

Composant Technologie RĂ´le

Framework

Spring Boot 4.1.0-M1 (WebFlux réactif sur Netty) — Java 25

REST API non bloquante, injection de dépendances

Contrat API

OpenAPI spec-first (openapi.yaml → openapi-generator)

Interfaces CvsApi, AiApi, CvMatchingApi, FilesApi implémentées par les controllers

Persistance documents

Spring Data MongoDB — MongoRepository bloquant, isolé sur Schedulers.boundedElastic() dans les adapters

Contenu des CV : collections cvDocuments, cvDocumentVersions (plafond configurable, 10 par défaut), cvVariants, appConfig

Persistance SQL

R2DBC (réactif) + Liquibase — PostgreSQL

Référentiel relationnel : users, resumes (métadonnées des fichiers uploadés) — aucune table CV côté SQL

Stockage objet

MinIO (S3) — bucket clarajob (MinioFileStorageService, SDK AWS S3 async)

Fichiers uploadés : resumes/, avatars/… — upload via POST /api/v1/files/upload

Lecture PDF

Apache PDFBox — lecture seule (PdfTextExtractor)

Extraction du texte brut Ă  l’import d’un CV PDF (aucune gĂ©nĂ©ration de PDF cĂ´tĂ© serveur)

IA générative

GeminiClient (WebClient) — provider Gemini ou Groq selon clarajob.ai.provider (défaut gemini-2.0-flash)

Reformulation, génération de CV/sections, chat Clara, structuration des imports, matching CV/offre

Cache / quota

Redis 7 (réactif)

Quota IA journalier par utilisateur (clé ai-usage:{userId}:{date}, fail-open) — adapter présent mais non branché (voir écarts ci-dessous) ; pas de sessions (JWT stateless)

Sécurité

Keycloak — resource server JWT (rôles realm_access, dont ADMIN)

Tous les endpoints CV sont authentifiés sauf GET /api/v1/cv-documents/shared/** (lecture publique des CV partagés)

Validation

Jakarta Validation

Contraintes sur les DTO d’entrĂ©e

Logging

SLF4J + Logback

Observabilité

Diagramme en couches

Architecture backend en couches

Lecture du diagramme :

  • PrĂ©sentation — 3 REST controllers spec-first : CvDocumentController (implĂ©mente CvsApi), AiController (AiApi), CvMatcherController (CvMatchingApi)

  • Application — 8 services : CvDocumentService, CvVersionService, CvVariantService, CvShareService, CvImportService, AiService, CvGenerationService, CvChatService — plus les DTOs de rĂ©ponse (CvDocumentResponse, CvVersionResponse, CvVariantResponse, CvShareResponse, CvPublicResponse…)

  • Domaine — CvDocument (aggregate root), CvDocumentVersion, CvVariant, value objects (CvDocumentId, CvVariantId, JobOfferReference…) et ports (CvDocumentRepository, CvDocumentVersionRepository, CvVariantRepository, AiClient, AiUsageQuotaPort, FileStorageService)

  • Infrastructure / Persistance — adapters : CvDocument/Version/Variant RepositoryAdapter qui enveloppent des MongoCv*Repository bloquants isolĂ©s sur boundedElastic(), AppConfigRepository (Mongo appConfig), MinioFileStorageService, GeminiClient, AiUsageQuotaRedisAdapter, PdfTextExtractor (PDFBox)

  • Bases & services externes — MongoDB (cvDocuments, cvDocumentVersions, cvVariants, appConfig), PostgreSQL (users, resumes — aucune table CV), Redis, MinIO, API Gemini/Groq

Chaque couche ne dépend que de la couche inférieure via les ports du domaine ; les adapters sont injectés dans les services par DI Spring.

Diagramme de composants

Composants backend et dépendances

Points clés :

  • CvDocumentController dĂ©lègue Ă  5 services (CvDocumentService, CvVersionService, CvVariantService, CvShareService, CvImportService) — pas de logique mĂ©tier dans la couche REST

  • CvImportService collabore avec FileStorageService (tĂ©lĂ©chargement MinIO), PdfTextExtractor (PDFBox), AiClient (structuration IA) et UserRepository/UserSkillRepository (import depuis le profil)

  • AiController → AiService / CvGenerationService / CvChatService → port AiClient → GeminiClient → API Gemini ou Groq ; CvMatcherController → CvMatcherService suit le mĂŞme chemin IA

  • CvVersionService lit/Ă©crit le plafond maxVersionsPerCv dans la collection Mongo appConfig (endpoints admin GET/PUT /api/v1/cv-documents/admin/config)

  • Aucune dĂ©pendance circulaire : le sens Controller → Service → Port → Adapter → Base est strict

Endpoints réellement exposés (préfixe /api/v1) :

Endpoint Chemin technique

POST /cv-documents · GET /cv-documents/me · GET/PUT/DELETE /cv-documents/{id}

CvDocumentService → CvDocumentRepository → Mongo cvDocuments

POST /cv-documents/import-pdf · POST /cv-documents/import-profile · POST /cv-documents/import/text

CvImportService (MinIO + PDFBox + IA) — voir séquence ci-dessous

GET/POST /cv-documents/{id}/versions · DELETE …/{versionId} · POST …/{versionId}/restore

CvVersionService → Mongo cvDocumentVersions (plafond via appConfig)

GET/POST /cv-documents/{id}/variants

CvVariantService → Mongo cvVariants

PUT /cv-documents/{id}/share · GET /cv-documents/{id}/share-status

CvShareService (token + slug de partage)

GET /cv-documents/shared/{token} · GET /cv-documents/shared/s/{slug} — publics, sans JWT

CvShareService.getPublicByToken/BySlug (réponse CvPublicResponse, header X-Robots-Tag: noindex)

GET/PUT /cv-documents/admin/config (rĂ´le ADMIN)

CvVersionService.getMaxVersions/updateMaxVersions → Mongo appConfig

POST /ai/reformulate · /ai/generate-cv · /ai/generate-section · /ai/chat · /ai/detect-skills

AiService / CvGenerationService / CvChatService → AiClient

POST /cv/match

CvMatcherController → CvMatcherService → AiClient

GET /cvs (liste) · DELETE /cvs/{cv_id}

seuls endpoints du contrat /api/v1/cvs réellement câblés sur CvDocumentService

POST /cvs · GET/PUT /cvs/{cv_id} · POST /cvs/{cv_id}/export · POST/DELETE /cvs/{cv_id}/share

501 Not Implemented — contrat OpenAPI conservé, implémentation portée par /cv-documents

Diagramme de sĂ©quence — Import d’un CV PDF

Séquence import PDF

Ce flux est reprĂ©sentatif du modèle dynamique : le frontend uploade d’abord le PDF dans MinIO (POST /api/v1/files/upload → fileKey), puis appelle POST /api/v1/cv-documents/import-pdf. Le controller reste passif ; CvImportService orchestre : contrĂ´le de propriĂ©tĂ© du fileKey (prĂ©fixe resumes/{userId}/), tĂ©lĂ©chargement MinIO (FileStorageService.download), extraction du texte avec PDFBox (PdfTextExtractor, isolĂ© sur boundedElastic()), structuration par l’IA (AiClient.generateJson avec le STRUCTURATION_PROMPT), rĂ©paration d’un Ă©ventuel JSON tronquĂ© puis normalizeImportedData, et enfin persistance (CvDocument.create → CvDocumentRepository.save → Mongo cvDocuments). La rĂ©ponse est un CvDocumentResponse (201 Created).

Important

Écarts assumĂ©s entre le contrat et l’implĂ©mentation :

  • Export PDF 100 % frontend : la gĂ©nĂ©ration PDF s’exĂ©cute cĂ´tĂ© navigateur (impression/canvas, useCvVectorExport.ts) ; l’endpoint POST /api/v1/cvs/{cv_id}/export renvoie 501 Not Implemented et aucun PDF gĂ©nĂ©rĂ© n’est stockĂ© (ni en base, ni dans MinIO). PDFBox cĂ´tĂ© serveur ne sert qu’Ă  lire les PDF importĂ©s.

  • Quota IA prĂ©sent mais non appelĂ© : la chaĂ®ne AiUsageQuotaPort → AiUsageQuotaRedisAdapter (compteur Redis journalier, fail-open) et AiUsageQuotaResolver existent, mais aucun service IA ne les invoque actuellement.

  • Branche morte CvDesignSettings : CvDesignController (/api/cv/{cvId}/design, hors contrat OpenAPI) et son repository R2DBC ciblent une table cv_design_settings absente des migrations Liquibase — code non fonctionnel, non migrĂ©.

Sync Gate 2 — Vérification track fonctionnel ↔ track technique

UC Chemin technique réel Statut

UC01 Créer

POST /api/v1/cv-documents → CvDocumentService.create → Mongo cvDocuments

âś… backend

UC02 Modifier

PUT /api/v1/cv-documents/{id} → CvDocumentService.update

âś… backend

UC03 Consulter

GET /api/v1/cv-documents/me · GET /api/v1/cv-documents/{id} → CvDocumentService.findByUserId/findById

âś… backend

UC04 Supprimer

DELETE /api/v1/cv-documents/{id} → CvDocumentService.delete (suppression physique, pas de soft delete)

âś… backend

UC05 Dupliquer

copie du contenu cĂ´tĂ© frontend puis POST /api/v1/cv-documents — pas d’endpoint dĂ©diĂ©

⚠️ frontend + UC01

UC06–UC08 Sections

sections Ă©ditĂ©es dans le JSON data du document → PUT /api/v1/cv-documents/{id} — pas d’API par section

âś… via UC02

UC09 Photo

POST /api/v1/files/upload → FileUploadController → MinioFileStorageService → MinIO

âś… backend

UC10 Template

champs templateLayout / accentColor / fontFamily / spacing du document → PUT /api/v1/cv-documents/{id}

âś… via UC02

UC11 Admin

GET/PUT /api/v1/cv-documents/admin/config (rôle ADMIN) → CvVersionService → Mongo appConfig — seul maxVersionsPerCv est administrable ; les templates sont définis côté frontend

⚠️ partiel

UC12 Aperçu

rendu côté frontend à partir de GET /api/v1/cv-documents/{id} — lecture seule côté backend

⚠️ frontend

UC13 Export PDF

génération côté navigateur — POST /api/v1/cvs/{cv_id}/export → 501

⚠️ frontend

UC14 Partager

PUT /cv-documents/{id}/share · GET /{id}/share-status → CvShareService ; lecture publique GET /shared/{token} et /shared/s/{slug}

âś… backend

UC15 Valider

Jakarta Validation sur les DTO + validation temps réel côté frontend — pas de service de validation dédié

⚠️ partiel

UC16 Sauvegarder

PUT /api/v1/cv-documents/{id} (déclenché par le frontend)

âś… backend

UC17 Restaurer

POST /cv-documents/{id}/versions puis POST …/{versionId}/restore → CvVersionService (plafond 10 versions par défaut, configurable)

âś… backend

UC18 Auto-save

timer côté frontend → PUT /api/v1/cv-documents/{id} — aucun scheduler backend

⚠️ frontend

UC19 Nettoyage

non implémenté : aucun @Scheduled ne purge les collections CV

❌ non couvert

Fonctionnalités réelles supplémentaires, hors référentiel UC : import PDF / profil / texte (CvImportService), variantes ciblées par offre (CvVariantService), IA générative (reformulation, génération, chat Clara, détection de compétences) et matching CV/offre (POST /api/v1/cv/match).

Verdict Sync Gate 2 : le cĹ“ur CRUD, le versionnage, le partage, l’upload et l’import IA ont un chemin technique complet sans dĂ©pendance circulaire. Les UC d’aperçu, d’export PDF et d’auto-save sont portĂ©s par le frontend (choix assumĂ©), UC19 reste Ă  implĂ©menter cĂ´tĂ© backend.