# Datamodel ## Entity-relatiemodel ```mermaid erDiagram USER ||--o{ SEARCH_PROFILE : owns SEARCH_PROFILE ||--o{ PROFILE_REVISION : versions SEARCH_PROFILE ||--o{ SCORE_RUN : evaluates SEARCH_PROFILE ||--o{ DIGEST_OUTBOX : receives SOURCE ||--o{ SOURCE_RUN : executes SOURCE ||--o{ RAW_DOCUMENT : retrieves SOURCE_RUN ||--o{ RAW_DOCUMENT : produces RAW_DOCUMENT ||--o{ JOB_SOURCE_ALIAS : evidences RAW_DOCUMENT ||--o| EMAIL_MESSAGE_RECORD : wraps EMPLOYER ||--o{ JOB_POSTING : publishes JOB_POSTING ||--o{ JOB_SOURCE_ALIAS : aliases JOB_POSTING ||--o{ FIELD_PROVENANCE : fields JOB_SOURCE_ALIAS ||--o{ FIELD_PROVENANCE : proves JOB_POSTING ||--o{ JOB_VERSION : versions JOB_POSTING ||--o{ SCORE_RUN : scored JOB_POSTING ||--o{ FEEDBACK : receives JOB_POSTING ||--o{ APPLICATION : tracked USER ||--o{ FEEDBACK : gives USER ||--o{ APPLICATION : owns ``` ## Entiteiten ### SearchProfile Bevat harde/soft voorkeuren, locatie, drempels, digesttijd en version. De JSON-velden blijven schematisch gedocumenteerd in `docs/api/INTERNAL_CONTRACTS.md` en gevalideerd in service/formcode. Belangrijke invarianten: - unieke naam per user; - recommendation threshold ≤ top threshold; - gewichten numeriek, niet-negatief en met positieve som; - coördinaten binnen bereik; - precies één actief profiel per user via `activate()`-serviceflow. ### Source Bronregister met `source_type`, `status` en onafhankelijk `policy`. Status betekent operationele toestand; policy betekent toestemming: - `candidate` — ontdekt, niet actief; - `trial` — beperkte proefcontrole; - `active` — normaal gepland; - `quarantined` — automatisch/manual geïsoleerd; - `paused/disabled` — niet plannen; - `allow/review/deny` — netwerktoegangsbesluit. Een `deny`-bron mag niet `active` zijn. ### RawDocument Tijdelijke immutable-ish fetchsnapshot. Bevat alleen begrensde tekstbody, headers op allowlist, hash, parserresultaat en retentie. Quarantaine verhindert cleanup tot inspectie. ### Employer Genormaliseerde werkgever/recruiteridentiteit. Uniek op normalized name + domain. `is_direct_employer` en `is_recruiter` zijn confidencegedreven kenmerken, geen juridisch oordeel. ### JobPosting Canonieke vacature met UUID, canonical key, content hash, status en genormaliseerde velden. De canonical key wordt uit external ID/URL of fallbackidentiteit afgeleid. De exacte key is uniek; fuzzy matching is alleen een kandidaatbeslissing. Statusflow: ```text new -> active -> uncertain -> removed \-> expired active/uncertain -> duplicate/quarantined (beheerflow) ``` ### JobSourceAlias Verbindt alle bronvermeldingen met één canonieke vacature. Bewaart externe ID, bronURL, parser, confidence en beperkte payload. De alias is het anker voor veldprovenance. ### FieldProvenance Per veld: extractiemethode, confidence, evidenceexcerpt en parserversie. Evidence is begrensd en mag geen geheime mailinhoud bevatten. ### JobVersion Snapshot per unieke contenthash. Bij inhoudswijziging wordt de vorige toestand bewaard; de huidige toestand krijgt eveneens een versie. ### ScoreRun Append-only score voor één job + profielversie. Bevat componentbijdragen, confidence, recommendation, redenen en evidence. Meerdere runs maken algoritme- en profielvergelijking mogelijk. ### Feedback Append-only useractie. Geen laatste-statekolom; views kunnen de laatste actie afleiden. Hierdoor blijft correctiehistorie behouden. ### Application Eén dossier per user/job. `snapshot` bewaart wat de gebruiker zag toen hij solliciteerde. `document_manifest` bevat alleen metadata/paden, geen willekeurige externe URL-executie. ### DigestOutbox Idempotente meldingseenheid met unieke dedupe key. Statusovergangen: pending → sent/failed; skipped bij ontbrekende recipient/configuratie. ## Indexstrategie Bestaand: - job title + region; - job status + valid_through; - alias source + external ID; - alias canonical URL; - source + raw contenthash; - profile + score descending + created_at; - bron next_run_at en healthdatums. Toekomstig bij meetbare noodzaak: - trigram/index voor fuzzy title/employer (PostgreSQL); - partial index op actieve vacatures; - latest score materialisatie; - JSON GIN uitsluitend voor werkelijk bevraagde keys. ## Migratiebeleid - schemawijzigingen via Django migrations; - destructieve wijzigingen in twee fasen; - grote backfills chunked en restartable; - productieback-up vóór migratie; - migratiecheck in CI; - rollback of forward-fix expliciet in taskplan.