Files
VacatureRadar/docs/architecture/SYSTEM_ARCHITECTURE.md
T
Jens b8091e59bd
deploy / deploy (push) Canceled after 0s
Initial deploy setup
2026-07-21 14:00:00 +02:00

160 lines
5.1 KiB
Markdown

# 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
```mermaid
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
```mermaid
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
```text
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 `JobPosting` schrijft;
- AI die `Feedback`, `Application`, `Source.policy` of hard rules wijzigt;
- task met verborgen businessregels die niet in een service testbaar zijn;
- model save-hook die netwerkverkeer uitvoert.
## 6. Verwerkingsflow
```mermaid
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_document` en `persist_draft` zijn 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:
1. indexes en querysetprofiling;
2. chunked scoring;
3. task queues per prioriteit;
4. per-domain rate limit en distributed lock;
5. materialized/latest scoreselectie;
6. 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`.