160 lines
5.1 KiB
Markdown
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`.
|