5.1 KiB
Systeemarchitectuur
1. Architectuurstijl
VacatureRadar gebruikt een modulaire Django-monoliet. De domeinen delen één relationele database maar hebben duidelijke model-, service-, task- en UI-grenzen. Dit houdt deployment en beheer eenvoudig op Unraid, terwijl de kernlogica testbaar en later splitsbaar blijft.
2. Context
flowchart LR
U[Gebruiker] -->|browser| W[VacatureRadar web]
M[Vacaturemailbox] -->|IMAP| C[Collectielaag]
E[Publieke werkgevers- en ATS-pagina's] -->|HTTP(S)| C
C --> P[Extractie en normalisatie]
P --> D[(PostgreSQL)]
D --> S[Scoring en dedupe]
S --> W
S --> N[Digest/outbox]
N -->|SMTP| U
O[Optionele Ollama] <--> S
3. Containerarchitectuur
flowchart TB
RP[Reverse proxy / LAN] --> WEB[web: Gunicorn + Django]
WEB --> PG[(PostgreSQL)]
WEB --> REDIS[(Redis)]
WORKER[Celery worker] --> PG
WORKER --> REDIS
WORKER --> NET[Goedgekeurde publieke bronnen]
BEAT[Celery beat] --> REDIS
OLLAMA[Ollama optioneel] <--> WORKER
MAIL[IMAP/SMTP optioneel] <--> WORKER
Containerrollen:
- web — authenticatie, dashboard, detailpagina's, profiel- en beheerflows;
- worker — bronfetches, parsing, scoring, lifecycle, mailbox en verzending;
- scheduler — periodieke planning, geen bedrijfslogica;
- postgres — canonieke data, historie en outbox;
- redis — broker/resultbackend, geen bron van waarheid;
- ollama — optioneel lokaal model, niet nodig voor kern.
4. Domeinmodules
apps/core
- liveness/readiness;
- dashboardaggregatie;
- systeemstatus;
- securityheaders en navigatiecontext.
apps/profiles
- user-scoped zoekprofielen;
- harde regels, soft preferences en scoregewichten;
- profielversies en gecontroleerde feedbackdelta's.
apps/sources
- bronregister en policy/status;
- veilige fetcher;
- adapters en source discovery;
- raw document, source run en e-mailrecord;
- Celery-orkestratie.
apps/jobs
- canonieke vacature- en werkgevermodellen;
- normalisatie, features, dedupe en provenance;
- scoreberekening en scorehistorie;
- feedback, lifecycle en sollicitatiedossier.
apps/notifications
- dagelijkse payloadselectie;
- idempotente outbox;
- rendering en SMTP-verzending.
5. Laagregels
views/templates -> application services -> domain models / adapter interfaces
Celery tasks -> application services -> domain models / adapter interfaces
adapters -> pure extraction DTOs (geen ORM writes)
fetcher -> policy + URL security (geen parserlogica)
AI service -> gestructureerde optionele features (geen beslissingsbevoegdheid)
Verboden afhankelijkheden:
- template/view die externe websites fetcht;
- adapter die direct
JobPostingschrijft; - AI die
Feedback,Application,Source.policyof hard rules wijzigt; - task met verborgen businessregels die niet in een service testbaar zijn;
- model save-hook die netwerkverkeer uitvoert.
6. Verwerkingsflow
sequenceDiagram
participant B as Beat
participant T as Source task
participant F as Safe fetcher
participant A as Adapter registry
participant P as Pipeline
participant DB as PostgreSQL
participant S as Scoring
B->>T: schedule due source
T->>DB: create SourceRun
T->>F: fetch approved URL
F->>F: policy + DNS/IP + redirect checks
F-->>T: bounded document / 304 / error
T->>DB: save RawDocument
T->>A: extract(document)
A-->>P: ExtractionResult
P->>P: normalize + sanitize + features
P->>DB: find exact/fuzzy existing job
P->>DB: upsert job, alias, evidence, version
P->>S: score for active profiles
S->>DB: append ScoreRun
T->>DB: finish SourceRun + schedule next
7. Consistentie en transacties
process_raw_documentenpersist_draftzijn transactioneel;- unieke sleutels beschermen races op canonieke vacature, e-mail en outbox;
- scorehistorie is append-only;
- raw document en source run vormen debugcontext;
- een mislukte individuele e-maillink wordt in het e-mailrecord vastgelegd zonder andere links te verliezen;
- externe calls gebeuren buiten lange database-locks waar mogelijk.
8. Schaalpad
De eerste schaalgrens ligt niet bij HTTP-requestvolume maar bij het aantal bronjobs. Tot ongeveer tienduizenden vacatures en honderden bronnen volstaat de monoliet. Optimalisaties vóór opsplitsing:
- indexes en querysetprofiling;
- chunked scoring;
- task queues per prioriteit;
- per-domain rate limit en distributed lock;
- materialized/latest scoreselectie;
- raw-bodycompressie/objectstorage.
Pas na aantoonbare bottleneck kan collection worker als aparte service worden afgesplitst. De adapter- en DTO-grenzen maken dat mogelijk zonder nu operationele complexiteit toe te voegen.
9. Deploymenttrust boundaries
- browser ↔ web: authenticated session + CSRF;
- web/worker ↔ database/Redis: intern containernetwerk;
- worker ↔ internet: alleen goedgekeurde URL's via safe fetcher;
- worker ↔ IMAP/SMTP/Ollama: expliciete environmentconfiguratie;
- reverse proxy ↔ internet: TLS, host filtering en toegangsbeleid buiten de app.
Zie SECURITY_ARCHITECTURE.md en docs/quality/THREAT_MODEL.md.