đŸ—“ïž Diagramme de Classes — CV-Builder ClaraJob

ModĂšle statique rĂ©el — extrait du code de clarajob-front-api et clarajob-front-gui

Note

đŸ“ș Diagrammes SVG de ce document

Vue interactive : index.html


Vue d’ensemble

Le modĂšle de classes du CV-Builder repose sur un choix structurant, visible dans le code :

  • Backend minimaliste et schemaless — un seul agrĂ©gat CvDocument porte tout le CV ; son contenu (data) est un Map<String,Object> stockĂ© tel quel dans MongoDB. Il n’existe aucune classe Java pour les sections (expĂ©rience, formation, compĂ©tence
).

  • Frontend porteur du modĂšle riche — c’est TypeScript qui type le contenu du CV (CvData, CvExperience, CvSkill
) ; le backend le transporte sans l’interprĂ©ter.

  • IA structurante — le schĂ©ma de fait du contenu est dĂ©fini par les prompts (STRUCTURATION_PROMPT de CvImportService, prompts de CvChatService / CvGenerationService) et validĂ© cĂŽtĂ© client par Zod (chat.validation.ts).

Organisation du backend : DDD par bounded context — com.clarajob.cvbuilder.{domain,application,infrastructure,presentation}, avec les contextes voisins ai (services IA), job (matching CV/offre) et user (fichiers resumes). L’isolation des couches est vĂ©rifiĂ©e par ArchUnit (CvBuilderArchTest).


📊 Diagramme UML Complet

Diagramme de classes UML — modĂšle rĂ©el backend + frontend

Lecture du diagramme :

  • Vert — domaine cvbuilder (agrĂ©gat, value objects, ports)

  • Bleu — services applicatifs et presentation (controllers spec-first OpenAPI)

  • Violet — contexte ai (port AiClient, GeminiClient bi-provider, services IA)

  • Orange — infrastructure de persistance (entities @Document, adapters, repositories Mongo bloquants)

  • Jaune — modĂšle TypeScript du frontend (types, store Pinia, composables)


📋 Dictionnaire des Classes — Backend

Domaine cvbuilder (package com.clarajob.cvbuilder.domain.model)

CvDocument (agrégat racine)

Responsabilité

CV du builder : identité du document, design, contenu schemaless, partage

Attributs

id: CvDocumentId, userId: Long, name (dĂ©f. « Mon CV »), templateLayout: String (dĂ©f. minimal), accentColor (dĂ©f. #FF4D2E), fontFamily (dĂ©f. dm-sans), spacing (dĂ©f. standard), showBadge: boolean, data: Map<String,Object>, version: int (constant Ă  1 — jamais incrĂ©mentĂ©), createdAt/updatedAt: Instant, shareToken (UUID Ă  la crĂ©ation), shareSlug (nullable), shareEnabled: boolean

Opérations

create(
) / reconstitute(
) (fabriques statiques, constructeur privé), update(
), enableSharing(), disableSharing(), updateSlug(slug) (regex ^[a-z0-9][a-z0-9-]{1,58}[a-z0-9]$)

Persistance

Collection MongoDB cvDocuments via CvDocumentEntity

UC associés

UC01–UC12, UC19

CvDocumentVersion

Responsabilité

Snapshot immuable d’un CV (versioning manuel + rotation)

Attributs

id: CvDocumentVersionId, cvDocumentId, userId, label (déf. « Version du yyyy-MM-dd »), data: Map, templateLayout, accentColor, createdAt

Opérations

create(
) / reconstitute(
) — aucune mutation

Persistance

Collection cvDocumentVersions, index composé cvDocumentId + createdAt desc ; rotation FIFO, max 10 par CV (clarajob.cv-builder.max-versions-per-cv, surchargeable à chaud via la collection appConfig)

UC associés

UC14

CvVariant

Responsabilité

Variante d’un CV maĂźtre adaptĂ©e Ă  une offre d’emploi

Attributs

id: CvVariantId, masterCvId, userId, name, jobOffer: JobOfferReference, deltas: Map<String,Object>, matchScore: Integer, createdAt/updatedAt

Opérations

update(name, deltas, matchScore)

Persistance

Collection cvVariants ; le VO jobOffer y est aplati en 4 champs (jobOfferTitle/Company/Url/Id)

UC associés

UC15

Value Objects

Records

CvDocumentId(String), CvDocumentVersionId(String), CvVariantId(String) — validation non-blank ; JobOfferReference(title, company, url, jobId)

Ports du domaine (domain.repository)

CvDocumentRepository

save, findById, findByUserId: Flux, deleteById, findByShareToken, findByShareSlug, existsByShareSlug

CvDocumentVersionRepository

save, findByCvDocumentId, findById, deleteById, countByCvDocumentId, deleteOldestByCvDocumentId

CvVariantRepository

save, findById, findByMasterCvId, deleteById, countByMasterCvId

Contenu du CV — schĂ©ma de fait de data

Aucune classe Java : le schéma est défini par les prompts IA et les types TypeScript.

identity      { firstName, lastName, title, email, phone, city, linkedin, portfolio }
summary       String (HTML TipTap)
experiences   [{ id, position, company, startDate, endDate, description, skills[] }]
education     [{ id, degree, institution, startYear, endYear, mention }]
skills        [{ name, level 1-5, pleasure, seniority, potential, category, active }]
languages     [{ name, level CECRL }]
projects      [{ id, name, description, url, skills[] }]
certifications[{ id, name, issuer, year }]
customSections?, fontFamily, fontSize?, spacing, sectionOrder?, hiddenSections?

category ∈ frontend | backend | devops | database | tools | soft-skills | methodology | other.

Services applicatifs (application.service)

Service Responsabilités & méthodes (toutes réactives Mono/Flux)

CvDocumentService

CRUD du CV — create, findByUserId, findById, update, delete
Inject : CvDocumentRepository — contrĂŽle d’ownership manuel (userId ≠ → DomainException)

CvVersionService

Versioning — createVersion, listVersions, restoreVersion, deleteVersion, getMaxVersions, updateMaxVersions (admin)
Inject : CvDocumentRepository, CvDocumentVersionRepository, CvBuilderConfig, AppConfigRepository
Rotation FIFO : count ≄ max → deleteOldestByCvDocumentId

CvVariantService

Variantes — create, findByMasterCvId, findById, update, delete
Inject : CvVariantRepository — ⚠ userId acceptĂ© mais non vĂ©rifiĂ© (pas d’ownership)

CvShareService

Partage — updateShare, getShareStatus, getPublicByToken, getPublicBySlug
Inject : CvDocumentRepository — slug dĂ©jĂ  pris → 409 CONFLICT

CvImportService

Imports — importFromProfile, importFromPdf, importFromText
Inject : FileStorageService (MinIO), AiClient, CvDocumentRepository, UserRepository, UserSkillRepository
Chaüne PDF : download MinIO → PdfTextExtractor (PDFBox, boundedElastic) → generateJson(STRUCTURATION_PROMPT, 120 000 tokens) → attemptFixTruncatedJson → normalizeImportedData

DTOs — application : CvDocumentResponse, CvVersionResponse, CvVariantResponse, CvShareResponse(shareEnabled, shareToken, shareSlug, tokenUrl, slugUrl), CvPublicResponse ; presentation : SaveCvRequest, UpdateCvRequest, CreateVersionRequest, CreateVariantRequest, UpdateVariantRequest, UpdateShareRequest, ImportPdfRequest, UpdateCvBuilderConfigRequest.

Presentation (spec-first OpenAPI)

CvDocumentController

implements CvsApi (interface gĂ©nĂ©rĂ©e depuis openapi.yaml) — tous les endpoints /api/v1/cv-documents/ (CRUD, imports, versions, partage, variantes, config admin)
Inject : les 5 services ci-dessus + AuthenticatedUserResolver (JWT → userId), CvApiMapper
Le contrat parallĂšle /api/v1/cvs n’est implĂ©mentĂ© que pour listMyCvs et deleteCv — createCv, getCvById, updateCv, exportCv, shareCv, unshareCv →
501 Not Implemented**

CvApiMapper

Adapte CvDocumentResponse au contrat /api/v1/cvs : UUID synthétique (nameUUIDFromBytes), CVStatus.READY forcé, shareViewsCount = 0 (aucun compteur réel)

Contexte ai

AiClient (port)

generate(sys, user[, maxTokens]), generateJson(sys, user, maxTokens)

GeminiClient

ImplĂ©mentation unique, bi-provider (clarajob.ai.provider ∈ gemini | groq — pas de classe GroqClient)
Gemini : POST /v1beta/models/{model}:generateContent (JSON mode via responseMimeType) ; Groq : POST /openai/v1/chat/completions (response_format: json_object)
generate() → fallback texte d’origine en cas d’erreur ; generateJson() → propage l’erreur

AiService

reformulate(text, context ∈ experience|summary|project, tone ∈ factual|creative|formal|human), detectSkills (max 12) — garde AiRefusalDetector → 422

CvGenerationService

generateFullCv(sector, experienceLevel, tone, freeText) → Map JSON (8192 tokens ; parse KO → 502) ; generateSection(section, context, tone, cvSummary) (1024 tokens)

CvChatService

chat(message, cvData, history) → ChatResponse(message, actions[]) via generateJson (4096 tokens)
Persona « Clara » ; 8 types d’actions (update_identity, update_section, set_experiences, set_education, set_skills, set_languages, add_skill/remove_skill, set_full_cv)

AiConfig / AiTokenBudgetProperties

Provider, clé, modÚle (déf. gemini-2.5-flash) ; budgets par usage : reformulate 1024, generateSection 1024, cvChat 4096, generateFullCv 8192, cvImportStructuration 120 000
⚠ AiUsageQuotaResolver (Redis, ai-usage:{userId}:{yyyyMMdd}) existe mais checkAndConsume n’est appelĂ© nulle part — quota inactif en pratique

Contexte job — CvMatcherService

Matching CV ↔ offre purement algorithmique (aucune IA) : normalisation NFD, extraction de mots-clĂ©s (regex [\p{L}\p{N}+#]+), stop-words FR/EN, score = matched/total × 100. Endpoint public POST /api/v1/cv/match.

Infrastructure de persistance

Classe RĂŽle

CvDocumentEntity
CvDocumentVersionEntity
CvVariantEntity
AppConfigEntity

@Document MongoDB (cvDocuments, cvDocumentVersions, cvVariants, appConfig) ; index uniques sparse sur shareToken / shareSlug

CvDocumentRepositoryAdapter
CvDocumentVersionRepositoryAdapter
CvVariantRepositoryAdapter

ImplĂ©mentent les ports du domaine en enveloppant les repositories Spring Data bloquants (MongoCvDocumentRepository
 extends MongoRepository) dans Mono.fromCallable(
).subscribeOn(Schedulers.boundedElastic()) — aucun ReactiveMongoRepository dans le projet

CvDocumentEntityMapper
CvDocumentVersionEntityMapper
CvVariantEntityMapper

Mappers statiques manuels entitĂ© ↔ domaine (pas de MapStruct pour le CV builder)

Important

Branche morte — Ă  ne pas confondre avec le modĂšle actif : CvDesignSettings (+ CvDesignController, CvDesignApplicationService, entitĂ© R2DBC cv_design_settings) est un sous-systĂšme non dĂ©ployable : la migration Liquibase n’est pas incluse dans changelog-master.xml et sa clĂ© Long cvId est incompatible avec l’ObjectId Mongo de CvDocument.


📋 Dictionnaire — Modùle Frontend (TypeScript)

Types du CV (src/types/cv-builder.types.ts)

CvData

identity, summary, experiences[], education[], skills[], languages[], projects[], certifications[], customSections?, fontFamily, fontSize?, spacing, sectionOrder?, hiddenSections?

CvIdentity

firstName, lastName, title, email, phone, city, linkedin, portfolio

CvExperience

id, position, company, startDate, endDate, description, skills?

CvEducation

id, degree, institution, startYear, endYear, mention

CvSkill

name, level, pleasure, seniority, potential (4 dimensions notĂ©es 1–5), category, active

CvLanguage / CvProject / CvCertification

{name, level} / {id, name, description, url, skills?} / {id, name, issuer, year}

CvCustomSection / CvCustomEntry

{id, title, icon, entries[]} / {id, title, subtitle, description}

Unions

CvLayout (57 valeurs = 57 composants de layout), CvStyleTag (mod|min|cre|cls|tec|ele), CvSector (9), CvExperienceLevel (4), CvTone (4), CvFontFamily (6 : dm-sans, syne, playfair, jetbrains, lora, outfit), CvFontSize (3), CvSpacing (compact|standard|airy), CvEditorSection (8)

CvTemplate / CvPalette

{id, name, layout, palette, tags, ats, popularity} / {id, name, hex} — 1140 templates gĂ©nĂ©rĂ©s (57 layouts × 20 palettes, generateTemplates())

Store et logique applicative

useCvBuilderStore (Pinia, id cvBuilder)

Store unique du builder. State : cvData, currentCvId, savedCvs, selectedTemplate, accentColor, activeSection, showBadge, lastSavedAt

Actions : CRUD des entrées de chaque section, addSkill/removeSkill (cascade expériences + projets), setFontFamily/Size/Spacing, setSectionOrder, toggleSectionVisibility, createNewCv, autoSave, loadExistingCv, deleteExistingCv

useAiChat (composable singleton)

État du chat Clara hors Pinia (module-level) : messages, actions en attente (_pendingActions — appliquĂ©es uniquement aprĂšs validation utilisateur), undoStack (snapshots mĂ©moire cvSnapshotManager, max 20), cooldown 5 s, conversations persistĂ©es en localStorage (ConversationStorage, 50 × 200 messages)

useCvScoring

Score client pur : 8 sections notĂ©es /10 → total %, score ATS par bonus fixes, dĂ©tection de 12 clichĂ©s, conseils contextuels — aucune validation bloquante de formulaire

useCvTemplates / useCvVectorExport

Galerie 57 × 20 avec filtres / export PDF vectoriel par window.print() (CvPrintDocument tĂ©lĂ©portĂ©, @page A4) — le chemin raster html2canvas+jsPDF existe mais n’est pas branchĂ©

Services API

cvDocumentService, cvVersionService, cvShareService, variantService, cvAiService, chatService, aiService — axios (httpClient) → /api/v1/**


🔗 Relations ClĂ©s

De Vers Relation

CvDocument

CvDocumentVersion

1 → 0..10 (par cvDocumentId ; rotation FIFO)

CvDocument

CvVariant

1 → * (par masterCvId)

CvVariant

JobOfferReference

1 → 1 (VO embarquĂ©, aplati en persistance)

CvDocument.data

CvData (TS)

miroir schemaless ↔ typĂ© (contrat implicite via prompts + Zod)

Services applicatifs

Ports du domaine

dépendance par interface (hexagonal)

Adapters

MongoRepository bloquants

délégation + boundedElastic

CvImportService / services IA

AiClient → GeminiClient

port/adapter, bi-provider Gemini|Groq

useCvBuilderStore.cvData

Backend

upsert complet PUT /cv-documents/{id} (autosave debounce 2 s)

🔎 TraçabilitĂ© UC → Classes

UC# Nom Classes principales

UC01

Créer un CV depuis zéro

ScratchWizardModal → CvGenerationService.generateFullCv → GeminiClient → useCvBuilderStore

UC02

Importer un CV PDF

chatService.validatePdfSize → MinIO → CvImportService.importFromPdf → PdfTextExtractor → AiClient

UC03

Importer depuis le profil

CvImportService.importFromProfile → UserRepository + UserSkillRepository (+ merge PDF)

UC04

Consulter mes CV

CvListScreen → cvDocumentService.getMyCvDocuments → CvDocumentService.findByUserId

UC05

Supprimer un CV

CvDocumentService.delete → CvDocumentRepositoryAdapter

UC06

Modifier le contenu

8 *Form + CustomSectionForm → useCvBuilderStore.cvData → CvDocumentService.update

UC07

Réorganiser / masquer

SectionOrderControl (sortablejs) / SectionVisibilityControl → sectionOrder/hiddenSections

UC08

Choisir un template

useCvTemplates.generateTemplates (57×20) → selectTemplate → templateLayout

UC09

Personnaliser le design

ColorCustomizer / StyleControls → accentColor, fontFamily, spacing, showBadge

UC10

Prévisualiser

CvMiniPreview (57 layouts) / CvPagedPreview (A4 595×842) / CvLivePreview (debounce 300 ms)

UC11

Exporter en PDF

EditorPreviewPanel → useCvVectorExport.exportVectorPdf → window.print() + CvPrintDocument

UC12

Partager le CV

SharePanel → CvShareService.updateShare → enableSharing/updateSlug

UC13

Consulter un CV partagé

CvPublicView → getPublicByToken/getPublicBySlug (public) → CvPublicResponse

UC14

Gérer les versions

CvVersionsScreen → CvVersionService (FIFO 10 ; admin : updateMaxVersions → appConfig)

UC15

Adapter Ă  une offre

CvAdaptScreen → CvMatcherService.match (algorithmique) → CvVariantService.create

UC16

Discuter avec Clara

AiCoachTab → useAiChat → CvChatService.chat → actions validĂ©es → cvSnapshotManager

UC17

Reformuler / générer

RichTextEditor → AiService.reformulate / CvGenerationService.generateSection

UC18

Score et conseils

useCvScoring → CvAtsScreen / EditorSidebar / InsightsSidebar (client pur)

UC19

Sauvegarde automatique

watchDebounced 2 s (CvWorkspaceView) → autoSave → CvDocumentService.update


📊 RĂ©sumĂ© Statistique (comptĂ© dans le code)

Backend — domaine cvbuilder

3 agrégats/entités (CvDocument, CvDocumentVersion, CvVariant) + 4 value objects + 3 ports

Backend — services

5 services cvbuilder + 3 services IA + CvMatcherService

Backend — persistance

4 collections MongoDB, 3 adapters, 3 mappers statiques ; 0 table SQL pour le CV

Backend — controllers

CvDocumentController (21 endpoints réels), AiController (5), CvMatcherController (1)

Frontend — types

12 interfaces + 10 unions (CvLayout : 57 valeurs)

Frontend — store

1 store Pinia (cvBuilder) + 3 composables clés (useAiChat, useCvScoring, useCvTemplates)

Frontend — rendu

57 composants de layout, 1140 templates gĂ©nĂ©rĂ©s (57 × 20 palettes)

Cas d’utilisation tracĂ©s

19/19 (UC01–UC19)


⚠ Écarts assumĂ©s (documentĂ©s, prĂ©sents dans le code)

  • CvDocument.version n’est jamais incrĂ©mentĂ© (toujours 1) — le versioning rĂ©el passe par cvDocumentVersions.

  • CvVariantService ne vĂ©rifie pas l’ownership (userId ignorĂ©).

  • Le quota IA (Redis) est cĂąblĂ© mais jamais appelĂ© par AiController.

  • Le contrat OpenAPI /api/v1/cvs (export, share, crĂ©ation) est majoritairement en 501 — la vraie API est /api/v1/cv-documents.

  • CvDesignSettings (PostgreSQL) est une branche morte non migrĂ©e.


đŸ‘ïž Comment Consulter ce Diagramme

  1. Ouvrir classes-uml-diagram.svg dans le navigateur et zoomer par zone de couleur.

  2. Ou gĂ©nĂ©rer le site : ./gradlew asciidoctor puis naviguer CV-Builder → Diagrammes de Classes.

  3. Croiser avec les 19 UC via le tableau de traçabilité ci-dessus.

Ce document est aligné sur le code au 2026-07-29 (branches develop de clarajob-front-api / clarajob-front-gui).