# Technische Invulling — Alle Componenten (C01–C43) > **Type**: Technisch Referentiedocument > **Versie**: 1.0 > **Datum**: 2026-02-14 > **Doelgroep**: Developers, architecten, LLM-agents > **Doel**: Eén overzicht van alle 43 componenten, ingedeeld per architectuurlaag (frontend, API, backend, middleware, infra), met concrete technologiekeuzes per component > **Relatie**: Consolideert alle keuzes uit de overige architectuurdocumenten --- ## Leeswijzer Dit document is het **technische naslagwerk** — geen strategie, geen rationale, puur de invulling. Per component staat: - **Waar** het draait (frontend / API / backend / middleware / infra) - **Welke** technologie, library of service - **Welke bestanden** er ontstaan (file structure) - **Dependencies** (npm packages of externe services) De architectuurkeuzes en rationale staan in de andere documenten. Dit document geeft antwoord op de vraag: **"wat heb ik nodig om dit te bouwen?"** --- ## Technologie Stack — Overzicht ### Runtime & Frameworks | Technologie | Versie | Doel | |-------------|--------|------| | Node.js | **22 LTS** | Runtime voor beide services | | Next.js | **16.1 LTS** (App Router) | Frontend + API routes (EPD Service + Intent Platform) | | TypeScript | 5.x | Type safety overal | | React | **19** | UI rendering | ### Database & Data | Technologie | Versie | Doel | |-------------|--------|------| | PostgreSQL | **17** | Primaire database (self-hosted, Docker) | | Drizzle ORM | latest | Type-safe database access, SQL-first (**RLS alleen in handmatige SQL**) | | pgvector | 0.7+ | Vector embeddings voor RAG pipeline | | pg_trgm | (ingebouwd) | Trigram similarity search | | **Valkey** | **8+** | Job queue (BullMQ), caching — Redis-compatible, open source (Linux Foundation) | ### Auth & Security | Technologie | Versie | Doel | |-------------|--------|------| | Keycloak | **26.5** | SSO/SAML, MFA, rolbeheer, multi-tenancy | | Auth.js (NextAuth) | v5 | Keycloak integratie in Next.js | | node:crypto | (ingebouwd) | AES-256-GCM field encryption, HMAC signing | ### AI & Spraak | Technologie | — | Doel | |-------------|---|------| | **Vercel AI SDK** | `@ai-sdk/anthropic` | Intent classificatie, samenvattingen, chat — provider-agnostisch, native Next.js streaming | | Deepgram API | — | Streaming speech-to-text (Nederlands) | ### UI & Styling | Technologie | Versie | Doel | |-------------|--------|------| | Tailwind CSS | **4.x** | Utility-first styling (5x sneller builds, zero-config) | | shadcn/ui | latest | Accessible component library | | Lucide React | latest | Icon library | | Zustand | **5.x** | Client-side state management | | date-fns | **4.x** | Datumformattering (NL locale) | | Zod | **4.x** | Runtime validatie op API boundaries | | Fuse.js | latest | Client-side fuzzy search | ### DevOps & Monitoring | Technologie | Versie | Doel | |-------------|--------|------| | Docker + Docker Compose | latest | Containerisatie | | Caddy | 2 | Reverse proxy, automatische HTTPS | | Docker Compose + GitHub Actions | — | Deployment pipeline (Coolify is nog beta — niet geschikt voor productie healthcare) | | Hetzner VPS | — | Hosting (Amsterdam, EU data residency) | | Pino | **10.x** | Structured JSON logging | | OpenTelemetry | latest | Traces, metrics | | BullMQ | 5.x | Job queue, scheduled jobs | | Bull Board | latest | Job monitoring dashboard | | Postal | latest | Self-hosted email | --- ## Twee Services — Deployment Architectuur ``` ┌─────────────────────────────────────────────────────────────────┐ │ CADDY (:443) — reverse proxy, TLS termination │ ├─────────────────┬───────────────────┬───────────────────────────┤ │ │ │ │ │ app.domein.nl │ platform.domein.nl│ auth.domein.nl │ │ ▼ │ ▼ │ ▼ │ │ EPD SERVICE │ INTENT PLATFORM │ KEYCLOAK │ │ Next.js :3000 │ Next.js :3001 │ :8080 │ │ │ │ │ │ Frontend (SSR) │ Platform API │ OIDC / SAML │ │ API Routes │ Intent Engine │ User management │ │ EPD Business │ Knowledge Layer │ Realm per tenant │ │ Logic │ Connector API │ │ │ │ │ │ ├─────────────────┴───────────────────┴───────────────────────────┤ │ │ │ PostgreSQL 17 :5432 Valkey :6379 Postal :25 │ │ ├── epd_db (BullMQ + cache) (self-hosted │ │ ├── platform_db email) │ │ └── keycloak_db │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## FRONTEND — Componenten Alles wat in de browser draait: React components, client-side state, UI interactie. ### C01 — Traditionele EPD UI | Eigenschap | Waarde | |------------|--------| | **Laag** | Frontend (EPD Service) | | **Technologie** | Next.js App Router, React Server Components + Client Components | | **Styling** | Tailwind CSS + shadcn/ui | | **Routing** | `app/epd/` directory (file-based routing) | | **Data fetching** | Server Components met `async` + Drizzle queries | | **Forms** | React Hook Form + Zod validation | | **Icons** | Lucide React | | **Datum** | date-fns met `nl` locale | ``` app/epd/ ├── dashboard/page.tsx # Hoofddashboard ├── verpleegrapportage/ # Overdracht overzicht │ └── rapportage/page.tsx # Tijdlijn view ├── patients/[id]/page.tsx # Patiëntdossier ├── agenda/page.tsx # Kalender (FullCalendar) ├── clients/page.tsx # Cliëntbeheer ├── behandelplan/[id]/page.tsx # Behandelplan detail ├── medicatie/page.tsx # Medicatieoverzicht ├── facturatie/page.tsx # ZPM prestatie-overzicht ├── admin/ # Beheer (tenant admin) │ ├── users/page.tsx │ ├── workflows/page.tsx │ └── protocols/page.tsx └── layout.tsx # Sidebar, header, notificatie-badge ``` **npm dependencies**: `next`, `react`, `tailwindcss`, `@radix-ui/*` (via shadcn), `lucide-react`, `date-fns`, `react-hook-form`, `@hookform/resolvers`, `zod` --- ### C02 — Intent Command Center | Eigenschap | Waarde | |------------|--------| | **Laag** | Frontend (EPD Service, embed in Intent Platform) | | **Technologie** | React Client Components | | **Activatie** | ⌘K (keyboard shortcut) | | **Invoer** | Tekst + spraak (C03) | | **Submit** | ⌘Enter | | **Streaming** | Server-Sent Events (SSE) voor AI responses | ``` components/cortex/command-center/ ├── command-center.tsx # Container, focus management ├── command-input.tsx # Tekst/spraak input, ⌘K listener ├── context-bar.tsx # Actieve patiënt, afdeling context ├── canvas-area.tsx # Render action blocks └── result-stream.tsx # SSE stream consumer ``` **npm dependencies**: (onderdeel van React app, geen extra deps) --- ### C03 — Spraak Input | Eigenschap | Waarde | |------------|--------| | **Laag** | Frontend + API | | **Technologie** | Web Audio API (browser) → Deepgram API (server) | | **Protocol** | WebSocket (streaming transcriptie) | | **Taal** | Nederlands (nl-NL) | ``` components/cortex/voice/ ├── voice-button.tsx # Microfoon toggle ├── audio-recorder.ts # Web Audio API capture └── transcript-stream.ts # WebSocket naar /api/deepgram ``` **npm dependencies**: geen extra (Web Audio API is browser-native) **Externe service**: Deepgram API (betaald, per minuut) --- ### C04 — Block/Artifact Systeem | Eigenschap | Waarde | |------------|--------| | **Laag** | Frontend | | **Technologie** | React components, composable blocks | | **Doel** | Render formulieren, voorbeelden, resultaten na intent classificatie | ``` components/cortex/blocks/ ├── dagnotitie-block.tsx # Rapport formulier (prefilled) ├── search-results-block.tsx # Zoekresultaten lijst ├── appointment-block.tsx # Afspraak formulier ├── overdracht-block.tsx # AI samenvatting view ├── nudge-block.tsx # Protocol suggestie card ├── intake-block.tsx # Intake navigatie └── block-renderer.tsx # Dynamisch block type → component mapping ``` **npm dependencies**: geen extra --- ### C13 — Regelvalidatie UI (Admin) | Eigenschap | Waarde | |------------|--------| | **Laag** | Frontend (Admin sectie EPD Service) | | **Technologie** | React, shadcn/ui DataTable | | **Doel** | Beheer protocol rules: goedkeuren, afwijzen, bewerken | ``` app/epd/admin/protocols/ ├── page.tsx # Overzicht alle regels ├── [id]/page.tsx # Detail + bewerken └── import/page.tsx # Bulk import uit kennisbronnen ``` --- ## API LAAG — Routes & Endpoints Alle HTTP endpoints. Draaien server-side in Next.js API routes (`app/api/`). ### EPD Service API Routes ``` app/api/ ├── reports/route.ts # C15 — CRUD rapportages ├── overdracht/ │ ├── route.ts # C16 — Patiëntlijst voor overdracht │ ├── [patientId]/route.ts # C16 — Patient detail │ └── generate/route.ts # C16 — AI samenvatting genereren ├── behandelplan/route.ts # C18 — CRUD behandelplannen ├── medicatie/route.ts # C19 — Medicatieoverzicht ├── patients/ │ ├── route.ts # C21 — CRUD patiënten │ └── search/route.ts # C21 — Patiënt zoeken ├── appointments/route.ts # C17 — CRUD afspraken ├── consents/route.ts # C22 — Toestemming registreren ├── facturatie/ │ ├── prestaties/route.ts # C40 — ZPM prestatie CRUD │ ├── trajecten/route.ts # C40 — DBC trajecten │ └── batches/route.ts # C40 — Declaratie batches ├── notifications/ │ ├── route.ts # C34 — Lijst + markeer gelezen │ ├── stream/route.ts # C34 — SSE real-time stream │ └── preferences/route.ts # C34 — Notificatie voorkeuren ├── workflows/ │ ├── route.ts # C35 — Actieve workflows │ ├── [id]/route.ts # C35 — Workflow detail │ └── [id]/transition/route.ts # C35 — State transitie ├── search/route.ts # C36 — Full-text search ├── webhooks/ │ ├── route.ts # C39 — CRUD webhook registraties │ └── [id]/deliveries/route.ts # C39 — Delivery log ├── bi/ │ ├── query/route.ts # C43 — BI queries │ └── export/route.ts # C43 — CSV export ├── fhir/ │ ├── Patient/[id]/route.ts # C28 — FHIR Patient export │ └── Patient/[id]/$everything/ # C28 — FHIR Bundle export ├── cds-hooks/ │ └── patient-view/route.ts # C29 — CDS Hooks cards ├── health/route.ts # C32 — Health check ├── audit/route.ts # C26 — Audit log query (admin) └── deepgram/route.ts # C03 — Spraak proxy ``` ### Intent Platform API Routes ``` app/api/platform/ ├── classify/route.ts # C06+C07 — Intent classificatie ├── chat/route.ts # C07 — Streaming chat (SSE) ├── transcribe/route.ts # C03 — Spraak-naar-tekst ├── nudges/route.ts # C10 — Actieve nudges ophalen ├── nudges/[id]/accept/route.ts # C10 — Nudge accepteren ├── protocols/route.ts # C11 — Protocol rules CRUD ├── connectors/route.ts # Connector registry beheer ├── capabilities/route.ts # Wat kan dit platform? └── health/route.ts # C32 — Health check ``` ### API Patronen — Thin Controller + Service Layer > **Architectuurbeslissing**: API routes zijn **thin controllers** — ze doen alleen HTTP-afhandeling (auth, validatie, response). Alle business logic zit in de **Service Layer** (`lib/services/`). Dit geeft ons de structuur van een NestJS-achtige backend zonder de overhead van een apart framework. **Waarom geen NestJS?** | Criterium | NestJS | Next.js + Service Layer | |-----------|--------|------------------------| | **Leercurve** | Hoog (DI, decorators, modules) | Laag (gewoon TypeScript functies) | | **Deployment** | Apart proces, eigen build pipeline | Eén Next.js deploy voor alles | | **Geschikt voor teamgrootte** | 5-20 developers | 1-5 developers | | **Structuur** | Afgedwongen door framework | Afgedwongen door conventie | | **Testbaarheid** | Excellent | Excellent (pure functions) | | **Migratiemogelijkheid** | — | Services zijn 1:1 verplaatsbaar naar NestJS later | **De regel**: als je team groeit naar 5+ developers en je hebt 200+ endpoints, migreer dan de Service Layer naar een losse NestJS of Hono API service. De services hoeven niet te veranderen — alleen de HTTP-laag eromheen. #### API Route (thin controller) ```typescript // app/api/reports/route.ts — THIN: alleen HTTP concerns import { NextResponse } from 'next/server'; import { auth } from '@/lib/auth'; import { reportService } from '@/lib/services/report.service'; import { CreateReportSchema } from '@/lib/types/report'; export async function POST(req: Request) { // 1. Auth (HTTP concern) const session = await auth(); if (!session) return NextResponse.json({ error: 'Niet ingelogd' }, { status: 401 }); // 2. Validatie (HTTP concern — parsing van request body) const body = CreateReportSchema.safeParse(await req.json()); if (!body.success) return NextResponse.json({ error: body.error.format() }, { status: 400 }); // 3. Delegeer alles naar service const result = await reportService.create(session.user, body.data); return NextResponse.json(result, { status: 201 }); } ``` #### Service (alle business logic) ```typescript // lib/services/report.service.ts — DIK: alle business logic, side effects, cross-cutting concerns import { withTenantContext } from '@/lib/multi-tenancy/tenant-context'; import { evaluateAccess } from '@/lib/access-control/evaluator'; import { logAuditEvent } from '@/lib/audit/logger'; import { webhookService } from '@/lib/services/webhook.service'; import { notificationService } from '@/lib/services/notification.service'; import { cacheService } from '@/lib/cache/cache-service'; import { db } from '@/lib/db'; import { reports } from '@/lib/db/schema'; export const reportService = { async create(user: AuthUser, data: CreateReport) { // 1. RBAC check const access = await evaluateAccess({ userId: user.id, action: 'report:create', resource: data }); if (!access.allowed) throw new ForbiddenError(access.reason); // 2. Business logic (binnen tenant context) const report = await withTenantContext(user.tenantId, user.id, async () => { return await db.insert(reports).values({ ...data, shiftDate: calculateShiftDate(new Date()), createdBy: user.id, tenantId: user.tenantId, }).returning(); }); // 3. Side effects (fire-and-forget, fouttolerant) await Promise.allSettled([ logAuditEvent({ entityType: 'report', entityId: report.id, action: 'create', userId: user.id }), webhookService.dispatch('report.created', user.tenantId, { reportId: report.id }), notificationService.send({ recipientUserId: data.assignedTo, category: 'rapportage', payload: report }), cacheService.invalidateByTag(`patient:${data.patientId}:reports`), ]); return report; }, async getByPatient(user: AuthUser, patientId: string, filters: ReportFilters) { await evaluateAccess({ userId: user.id, action: 'report:read', resource: { patientId } }); return cacheService.getOrSet( `reports:patient:${patientId}:${hashFilters(filters)}`, () => withTenantContext(user.tenantId, user.id, () => queryReports(patientId, filters)), { ttl: 60, tags: [`patient:${patientId}:reports`] } ); }, // ... update, softDelete, restore, getVersionHistory }; ``` #### Service Layer — Overzicht Alle EPD business logic services: ``` lib/services/ ├── report.service.ts # C15 — Rapportages CRUD, versioning, shift-date ├── overdracht.service.ts # C16 — AI samenvattingen, patiëntoverzicht ├── agenda.service.ts # C17 — Afspraken, conflictdetectie ├── behandelplan.service.ts # C18 — Behandelplannen, doelen ├── medicatie.service.ts # C19 — Medicatie, interactie-check ├── intake.service.ts # C20 — Intake, triage, workflow ├── patient.service.ts # C21 — Patiënt CRUD, zoeken, toewijzing ├── consent.service.ts # C22 — Wvggz + AVG toestemming ├── notification.service.ts # C34 — SSE, email, escalatie ├── workflow.service.ts # C35 — State machine, transitions ├── search.service.ts # C36 — Full-text search ├── webhook.service.ts # C39 — Dispatch, retry, signing ├── facturatie.service.ts # C40 — ZPM, DBC, declaraties └── bi.service.ts # C43 — BI queries, materialized views ``` Intent Platform services: ``` lib/cortex/ ├── registry/ # C05 — Intent Registry ├── reflex/ # C06 — Reflex Classifier ├── orchestrator/ # C07 — LLM Classificatie ├── entities/ # C08 — Entity Extractor ├── actions/ # C09 — Action System ├── protocol-engine/ # C10 — Nudge Engine └── knowledge/ # C11-C14 — Protocol Rules, RAG, Connectors ``` Gedeelde services (beide platforms): ``` lib/shared/ ├── cache/cache-service.ts # C37 — Caching met tag invalidatie ├── jobs/job-registry.ts # C38 — BullMQ job definities ├── auth/ # C23 — Auth helpers ├── multi-tenancy/ # C24 — Tenant context ├── access-control/ # C25 — RBAC + ABAC ├── audit/ # C26 — Audit trail ├── encryption/ # C27 — Field encryption ├── observability/ # C30 — Logging, metrics, traces └── health/ # C32 — Health checks ``` #### Shared Middleware Wrapper Om boilerplate in API routes te minimaliseren: ```typescript // lib/api/with-auth.ts — optionele wrapper voor herhalende patterns import { NextResponse } from 'next/server'; import { auth } from '@/lib/auth'; import { logger } from '@/lib/observability/logger'; type AuthHandler = (req: Request, user: AuthUser) => Promise; export function withAuth(handler: AuthHandler) { return async (req: Request) => { const session = await auth(); if (!session?.user) { return NextResponse.json({ error: 'Niet ingelogd' }, { status: 401 }); } try { return await handler(req, session.user); } catch (error) { if (error instanceof ForbiddenError) { return NextResponse.json({ error: error.message }, { status: 403 }); } logger.error({ err: error, path: req.url }, 'Onverwachte fout'); return NextResponse.json({ error: 'Interne serverfout' }, { status: 500 }); } }; } // Gebruik: // export const POST = withAuth(async (req, user) => { // const body = CreateReportSchema.parse(await req.json()); // const result = await reportService.create(user, body); // return NextResponse.json(result, { status: 201 }); // }); ``` **npm dependencies (API laag)**: `zod`, `drizzle-orm`, `drizzle-kit`, `pg`, `pino`, `node-redis`, `bullmq` --- ## BACKEND — Business Logic & Services Server-side logica die niet direct aan een HTTP endpoint hangt, maar door API routes en jobs wordt aangeroepen. ### C05 — Intent Registry | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (Intent Platform) | | **Technologie** | TypeScript config files → database (fase 3) | | **Doel** | Single source of truth voor alle intent definities | ``` lib/cortex/registry/ ├── intent-registry.ts # Registry class (in-memory, laadt uit config/DB) ├── types.ts # IntentDefinition interface └── definitions/ ├── dagnotitie.ts # Patronen, entities, artifact mapping ├── zoeken.ts ├── overdracht.ts ├── agenda-query.ts ├── create-appointment.ts ├── cancel-appointment.ts ├── reschedule-appointment.ts └── index.ts # Barrel export ``` **npm dependencies**: geen extra --- ### C06 — Reflex Classifier | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (Intent Platform) | | **Technologie** | Pure TypeScript, regex matching | | **Latency** | < 20ms | | **Patronen** | ~200 regex patronen, geladen uit Intent Registry | ``` lib/cortex/reflex/ ├── reflex-classifier.ts # classifyWithReflex(input) → ClassificationResult | null ├── pattern-matcher.ts # Regex engine met gewogen scoring └── confidence-scorer.ts # Berekent confidence op basis van match quality ``` **npm dependencies**: geen (pure TypeScript + RegExp) --- ### C07 — Orchestrator (LLM Classificatie) | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (Intent Platform) | | **Technologie** | Vercel AI SDK (`@ai-sdk/anthropic`, claude-sonnet-4-5) | | **Latency** | 250ms–3s | | **Activatie** | Alleen als Reflex confidence < 0.7 | | **Output parsing** | Zod schema validation op LLM response | ``` lib/cortex/orchestrator/ ├── orchestrator.ts # classifyWithOrchestrator(input, context) → ClassificationResult ├── prompt-builder.ts # System prompt + few-shot examples generatie ├── response-parser.ts # Zod parse van LLM JSON output └── streaming.ts # SSE streaming voor chat mode ``` **npm dependencies**: `ai`, `@ai-sdk/anthropic` --- ### C08 — Entity Extractor + Date Parser | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (Intent Platform) | | **Technologie** | TypeScript regex + Fuse.js (fuzzy matching) | | **Doel** | "Jan" → Patient #427, "morgen" → 2026-02-15 | ``` lib/cortex/entities/ ├── entity-extractor.ts # Extractie pipeline ├── patient-resolver.ts # Fuzzy match patiëntnamen (Fuse.js) ├── date-time-parser.ts # Nederlandse datum/tijd parsing └── category-resolver.ts # "medicatie" → NursingCategory ``` **npm dependencies**: `fuse.js` --- ### C09 — Action System | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (Intent Platform) → Frontend (C04) | | **Technologie** | TypeScript, EPD Connector interface | | **Doel** | Geclassificeerd intent → UI artifact instructie | ``` lib/cortex/actions/ ├── action-resolver.ts # Intent → ActionInstruction mapping ├── action-types.ts # ActionInstruction interface per intent └── prefill-builder.ts # Bouw prefill data voor formulier ``` --- ### C10 — Protocol Engine / Nudge | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (Intent Platform) | | **Technologie** | TypeScript rule evaluator, leest uit C11 Protocol Rules Store | | **Patroon** | Pure function: `(completedAction, patientContext, rules) → suggestions[]` | ``` lib/cortex/protocol-engine/ ├── evaluator.ts # evaluateRules() — kernfunctie ├── condition-matcher.ts # Match rule conditions tegen context └── nudge-formatter.ts # Formatting naar UI-compatible suggesties ``` --- ### C11 — Protocol Rules Store | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (Intent Platform — database) | | **Technologie** | PostgreSQL (platform_db), Drizzle schema | | **Tabel** | `protocol_rules` | ``` lib/knowledge/ ├── protocol-rule.ts # ProtocolRule interface + Drizzle schema ├── rule-repository.ts # CRUD operaties op rules └── rule-validator.ts # Validatie logica (status transitions) ``` --- ### C12 — RAG Pipeline | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (Intent Platform) | | **Technologie** | pgvector (PostgreSQL extensie), Anthropic Embeddings API | | **Doel** | Protocol documenten indexeren → vector search → context voor nudges | ``` lib/knowledge/rag/ ├── indexer.ts # Document → chunks → embeddings → pgvector insert ├── retriever.ts # Query → embedding → vector similarity search ├── chunk-splitter.ts # Document splitting strategie └── embedding-client.ts # Anthropic/OpenAI embeddings API wrapper ``` **npm dependencies**: `ai`, `@ai-sdk/anthropic` (of MedRoBERTa.nl voor Nederlandse klinische embeddings) **PostgreSQL extensie**: `pgvector` --- ### C14 — Kennisbron Connectors | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (Intent Platform) | | **Technologie** | TypeScript, HTTP clients voor externe bronnen | ``` lib/knowledge/connectors/ ├── connector-interface.ts # KennisbronConnector interface ├── ggz-standaarden.ts # GGZ Standaarden scraper/importer ├── venvn-richtlijnen.ts # V&VN richtlijnen ├── wetten-overheid.ts # Wetten.overheid.nl API └── document-parser.ts # PDF/HTML → tekst extractie ``` --- ### C15 — Rapportage Service | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (EPD Service) | | **Technologie** | Drizzle ORM, PostgreSQL | | **Features** | CRUD, versioning, soft delete, shift-date logica | ``` lib/services/rapportage/ ├── rapportage-service.ts # ReportService class ├── shift-calculator.ts # calculateShiftDate() — voor 07:00 = vorige dag └── version-manager.ts # Versioning bij updates ``` **Database tabel**: `reports` (met `version`, `previous_version_id`, `deleted_at`) --- ### C16 — Overdracht Service | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (EPD Service) | | **Technologie** | Drizzle ORM + Vercel AI SDK (`@ai-sdk/anthropic`) | | **Doel** | AI-samenvatting van rapportages per patiënt per shift | ``` lib/services/overdracht/ ├── overdracht-service.ts # Patiëntlijst, details, samenvattingen ├── summary-generator.ts # Claude API prompt + response parsing └── source-linker.ts # Bronverwijzingen in samenvatting ``` --- ### C17 — Agenda Service | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (EPD Service) | | **Technologie** | Drizzle ORM | | **Features** | CRUD, conflictdetectie, no-show tracking | ``` lib/services/agenda/ ├── agenda-service.ts # AppointmentService class └── conflict-detector.ts # Overlap check bij create/update ``` **Frontend**: FullCalendar (`@fullcalendar/react`, `@fullcalendar/daygrid`, `@fullcalendar/timegrid`) --- ### C18 — Behandelplan Service | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (EPD Service) | | **Technologie** | Drizzle ORM | ``` lib/services/behandelplan/ ├── behandelplan-service.ts # CRUD, versioning └── goal-tracker.ts # Behandeldoel voortgang ``` --- ### C19 — Medicatie Service | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (EPD Service) | | **Technologie** | Drizzle ORM | ``` lib/services/medicatie/ ├── medicatie-service.ts # Overzicht, wijzigingen └── interaction-checker.ts # Placeholder voor medicatie-interactie check ``` --- ### C20 — Intake Service | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (EPD Service) | | **Technologie** | Drizzle ORM + Workflow Engine (C35) | ``` lib/services/intake/ ├── intake-service.ts # Intake data management └── triage-rules.ts # Triage criteria ``` --- ### C21 — Patiënt Service | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (EPD Service) | | **Technologie** | Drizzle ORM, Fuse.js (zoeken), pg_trgm (server-side) | ``` lib/services/patient/ ├── patient-service.ts # CRUD, zoeken, toewijzing └── assignment-manager.ts # Patient-zorgverlener toewijzing ``` --- ### C22 — Toestemming Service | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (EPD Service) | | **Technologie** | Drizzle ORM | | **Doel** | Wvggz + AVG toestemmingsregistratie | ``` lib/services/consent/ ├── consent-service.ts # Registreren, intrekken, controleren └── wvggz-checker.ts # Wvggz-specifieke validaties ``` --- ### C34 — Notificatie Service | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (EPD Service) | | **Real-time** | Server-Sent Events (SSE) via API route | | **Email** | Postal (self-hosted SMTP) via BullMQ job | | **Escalatie** | BullMQ delayed jobs | ``` lib/services/notifications/ ├── notification-service.ts # send(), sendBulk(), getUnread() ├── sse-broadcaster.ts # SSE endpoint beheer ├── email-sender.ts # Postal SMTP integratie ├── escalation-manager.ts # Escalatie regels evalueren └── digest-builder.ts # Dagelijkse email samenvattingen ``` **npm dependencies**: `nodemailer` (voor Postal SMTP) **Database tabellen**: `notifications`, `notification_preferences`, `escalation_rules` --- ### C35 — Workflow Engine (State Machine) | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (EPD Service) | | **Technologie** | Eigen TypeScript state machine (~200 regels) | | **Geen externe library** | XState niet nodig voor server-side workflows | ``` lib/services/workflow/ ├── workflow-engine.ts # State machine core: transition(), getAvailable() ├── workflow-service.ts # Service layer: start, transition, query ├── deadline-monitor.ts # Check maxDurationDays overschrijdingen └── definitions/ ├── intake-ggz.ts # Intake workflow definitie ├── wvggz-procedure.ts # Wvggz workflow met wettelijke termijnen ├── mic-melding.ts # MIC/incident workflow ├── behandelplan-lifecycle.ts # Concept → Actief → Evaluatie └── index.ts ``` **Database tabellen**: `workflow_definitions`, `workflow_instances`, `workflow_transitions_log` (immutable) --- ### C36 — Full-Text Search Service | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (EPD Service — database) | | **Technologie** | PostgreSQL `tsvector` + `ts_rank_cd` | | **Taal** | Nederlandse stemmer (ingebouwd in PostgreSQL) | | **Indexering** | `GENERATED ALWAYS AS ... STORED` (automatisch bij write) | ``` lib/services/search/ ├── search-service.ts # search(), reindex() ├── query-builder.ts # Bouw SQL met filters, highlighting └── snippet-extractor.ts # ts_headline() voor context snippets ``` **Geen extra npm dependencies** — puur PostgreSQL functionaliteit --- ### C37 — Caching Layer | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (beide services) | | **Technologie** | Valkey (hergebruik van BullMQ instance) | | **Client** | node-redis (BullMQ gebruikt intern ioredis — dat is OK, twee clients naast elkaar) | | **Patroon** | Namespace-based keys met tenant isolatie, tag-based invalidatie | ``` lib/cache/ ├── cache-service.ts # get(), set(), getOrSet(), invalidateByTag() ├── namespaces.ts # TTL strategie per namespace └── key-builder.ts # Tenant-aware key generatie ``` **npm dependencies**: `redis` (node-redis) --- ### C38 — Scheduled Jobs Service | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (beide services) | | **Technologie** | BullMQ (repeatable jobs) + Redis | | **Monitoring** | Bull Board (web UI) | ``` lib/jobs/ ├── job-registry.ts # Registreer alle job handlers ├── job-worker.ts # BullMQ Worker setup ├── definitions/ │ ├── audit-partition.ts # Maandelijkse audit partitie aanmaken │ ├── workflow-deadlines.ts # Dagelijkse deadline check │ ├── notification-digest.ts # Dagelijkse email digest │ ├── zpm-batch.ts # Maandelijkse facturatie batch │ ├── protocol-sync.ts # Wekelijkse RAG re-index │ ├── cache-refresh.ts # Dashboard cache verversing │ └── escalation-check.ts # 5-minuten escalatie check └── bull-board.ts # Admin monitoring dashboard ``` **npm dependencies**: `bullmq`, `@bull-board/api`, `@bull-board/express` --- ### C39 — Webhook System | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (EPD Service) | | **Signing** | HMAC-SHA256 (`node:crypto`) | | **Retry** | BullMQ delayed jobs met exponential backoff | | **Geen PII in payloads** — alleen IDs | ``` lib/services/webhooks/ ├── webhook-service.ts # register(), dispatch(), retry() ├── webhook-signer.ts # HMAC-SHA256 signature generatie ├── delivery-manager.ts # Dispatch + retry via BullMQ └── payload-sanitizer.ts # Strip PII, alleen IDs behouden ``` **Database tabellen**: `webhook_registrations`, `webhook_delivery_log` --- ### C40 — ZPM Facturatie Service | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (EPD Service) | | **Technologie** | Drizzle ORM | | **Doel** | Zorgprestatie registratie, DBC trajecten, declaratie batches | ``` lib/services/facturatie/ ├── facturatie-service.ts # CRUD prestaties, trajecten, batches ├── prestatie-validator.ts # NZa validatieregels ├── batch-generator.ts # Maandelijkse batch generatie └── vecozo-interface.ts # Placeholder voor Vecozo koppeling ``` **Database tabellen**: `zorgprestaties`, `dbc_trajecten`, `declaratie_batches` --- ### C41 — Vecozo Connector (voorbereiding) | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (EPD Service) | | **Status** | Interface + mock — geen live koppeling | | **Vereisten** | UZI-certificaat, AGB-code, Vektis EI-standaard | ``` lib/integrations/vecozo/ ├── types.ts # VecozoConnector interface ├── mock-connector.ts # Mock implementatie voor development └── README.md # Aansluitvereisten documentatie ``` --- ### C42 — Zorgdomein Connector (voorbereiding) | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (EPD Service) | | **Status** | Interface + mock | ``` lib/integrations/zorgdomein/ ├── types.ts # ZorgdomeinConnector interface ├── mock-connector.ts # Mock implementatie └── README.md # Aansluitvereisten ``` --- ### C43 — BI Module | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (EPD Service — database) | | **Technologie** | PostgreSQL Materialized Views | | **Verversing** | `REFRESH MATERIALIZED VIEW CONCURRENTLY` via C38 scheduled job | ``` lib/services/bi/ ├── bi-service.ts # query(), refreshViews(), exportCSV() ├── views/ │ ├── mv-productie.sql # Productie per behandelaar per maand │ ├── mv-wachttijden.sql # Aanmelding → intake doorlooptijden │ └── mv-no-shows.sql # No-show percentage per maand └── csv-exporter.ts # Resultaten naar CSV ``` **Toekomstig**: Metabase (self-hosted) als visuele BI frontend bovenop dezelfde PostgreSQL --- ## MIDDLEWARE — Cross-Cutting Concerns Logica die door alle lagen heen loopt: auth, multi-tenancy, audit, access control, encryptie, observability. ### C23 — Auth + SSO | Eigenschap | Waarde | |------------|--------| | **Laag** | Middleware (beide services) | | **Technologie** | Keycloak (OIDC/SAML) + Auth.js v5 | | **SSO** | Azure AD federatie (standaard bij GGZ-instellingen) | | **MFA** | TOTP, WebAuthn (Keycloak native) | | **Session** | JWT tokens, 15 min timeout (NEN 7510) | ``` lib/auth/ ├── auth.ts # Auth.js v5 config met KeycloakProvider ├── middleware.ts # Next.js middleware: redirect als niet ingelogd ├── session.ts # Session helpers: getUser(), getRole(), getTenantId() └── guards.ts # Route guards: requireRole(), requirePermission() ``` **npm dependencies**: `next-auth@5`, `@auth/core` **Externe service**: Keycloak (self-hosted Docker container) --- ### C24 — Multi-tenancy | Eigenschap | Waarde | |------------|--------| | **Laag** | Middleware (database + applicatie) | | **Technologie** | PostgreSQL RLS + Drizzle tenant context helper | | **Model** | Shared database, `tenant_id` kolom op elke tabel | ``` lib/multi-tenancy/ ├── tenant-context.ts # withTenantContext() — zet PostgreSQL session vars ├── tenant-middleware.ts # Extract tenant_id uit JWT, inject in request └── tenant-config.ts # TenantSettings interface + laden uit DB ``` **Kern**: `SET app.current_tenant_id = '...'` per database connection → RLS policies filteren automatisch --- ### C25 — RBAC + ABAC Engine | Eigenschap | Waarde | |------------|--------| | **Laag** | Middleware (beide services) | | **Technologie** | TypeScript policy evaluator | | **RBAC** | Rollen uit Keycloak JWT (verpleegkundige, arts, psycholoog, admin, audit) | | **ABAC** | Context-afhankelijke policies (toewijzing, toestemming, tijdvenster, Wvggz) | ``` lib/access-control/ ├── evaluator.ts # evaluateAccess() → { allowed, reason } ├── policies/ │ ├── patient-access.ts # Basisbeleid patiënt inzage │ ├── wvggz-access.ts # Wvggz-specifieke restricties │ ├── report-write.ts # Wie mag rapportages schrijven │ └── admin-access.ts # Admin/audit specifiek ├── role-permissions.ts # RBAC matrix: rol → basis permissions └── types.ts # AccessPolicy, PolicyCondition interfaces ``` --- ### C26 — Audit Trail (NEN 7513) | Eigenschap | Waarde | |------------|--------| | **Laag** | Middleware (beide services) | | **Technologie** | PostgreSQL partitioned table + `node:crypto` (hash chain) | | **Immutabiliteit** | Triggers blokkeren UPDATE en DELETE | | **Partitionering** | Per maand, automatisch via C38 scheduled job | ``` lib/audit/ ├── logger.ts # logAuditEvent() — kernfunctie ├── hash-chain.ts # SHA-256 hash + previous_hash keten ├── query-builder.ts # Forensische queries (wie/wat/wanneer) └── anomaly-detector.ts # Anomalie: >50 dossiers in 1 uur ``` **Database tabel**: `audit_logs` (partitioned by range on timestamp) --- ### C27 — Field Encryption | Eigenschap | Waarde | |------------|--------| | **Laag** | Middleware (EPD Service) | | **Technologie** | AES-256-GCM via `node:crypto` | | **Gevoelige velden** | BSN, diagnoses, medicatie in audit logs | ``` lib/encryption/ ├── field-encryption.ts # encryptField(), decryptField() ├── bsn-handler.ts # storeBSN(): hash (lookup) + encrypted (weergave) └── key-management.ts # Key rotation helpers ``` **Geen extra npm dependencies** — `node:crypto` is ingebouwd --- ### C28 — FHIR Export Service | Eigenschap | Waarde | |------------|--------| | **Laag** | Middleware (EPD Service API) | | **Technologie** | TypeScript mapping functies | | **Standaard** | FHIR R4 | | **Strategie** | FHIR-compatible (export), niet FHIR-native (storage) | ``` lib/fhir/ ├── patient-mapper.ts # Interne Patient → FHIR Patient ├── observation-mapper.ts # Report → FHIR Observation ├── condition-mapper.ts # Diagnosis → FHIR Condition ├── bundle-builder.ts # $everything → FHIR Bundle └── types.ts # FHIR R4 type definities ``` --- ### C29 — CDS Hooks Service | Eigenschap | Waarde | |------------|--------| | **Laag** | Middleware (Intent Platform API) | | **Technologie** | CDS Hooks 1.0 spec implementatie | | **Doel** | Nudges aanbieden in CDS Hooks formaat voor externe EPD's | ``` lib/cds-hooks/ ├── hook-handler.ts # patient-view hook implementatie ├── card-builder.ts # Nudge → CDS Hooks Card mapping └── types.ts # CDS Hooks type definities ``` --- ### C30 — Observability | Eigenschap | Waarde | |------------|--------| | **Laag** | Middleware (beide services) | | **Logging** | Pino (structured JSON) → stdout → log aggregator | | **Metrics** | OpenTelemetry SDK → Grafana/Prometheus | | **Traces** | OpenTelemetry traces door hele pipeline | ``` lib/observability/ ├── logger.ts # Pino instance met PII filtering ├── pii-redactor.ts # Automatisch BSN, namen etc. redacten in logs ├── metrics.ts # OpenTelemetry meter: latency, errors, classificaties ├── tracer.ts # OpenTelemetry tracer setup └── cortex-metrics.ts # Cortex-specifiek: reflex hits, orchestrator fallbacks ``` **npm dependencies**: `pino`, `@opentelemetry/sdk-node`, `@opentelemetry/api`, `@opentelemetry/exporter-prometheus` --- ### C31 — Circuit Breaker / Resilience | Eigenschap | Waarde | |------------|--------| | **Laag** | Middleware (Intent Platform) | | **Technologie** | Eigen TypeScript implementatie (~50 regels) | | **Fallback** | AI niet beschikbaar → alleen Reflex (lokale patronen) | ``` lib/resilience/ ├── circuit-breaker.ts # callWithCircuitBreaker() generic └── degradation-levels.ts # Niveau 0 (volledig) → 1 (reflex-only) → 2 (minimaal) ``` --- ### C32 — Health Check Service | Eigenschap | Waarde | |------------|--------| | **Laag** | Middleware (beide services) | | **Technologie** | Next.js API route | | **Checks** | Database, Keycloak, Claude API, Deepgram, Redis | ``` lib/health/ ├── health-checker.ts # Parallel health checks └── capability-reporter.ts # Wat werkt? (graceful degradation status) ``` --- ### C33 — PII Filter | Eigenschap | Waarde | |------------|--------| | **Laag** | Middleware (Intent Platform) | | **Technologie** | TypeScript regex + sanitisatie | | **Doel** | Voorkom dat PII in AI prompts en logs terechtkomt | ``` lib/pii/ ├── pii-filter.ts # sanitizeForAI() — verwijder namen, BSN, etc. ├── log-sanitizer.ts # Pino serializer die PII redact └── patterns.ts # Regex patronen voor BSN, telefoonnummers, etc. ``` --- ## CONNECTIVITEITSLAAG — EPD Connectors De adapter-laag tussen Intent Platform en EPD systemen. ### Connector Interface (platform-breed) | Eigenschap | Waarde | |------------|--------| | **Laag** | Backend (Intent Platform) | | **Patroon** | Adapter pattern — elke EPD implementeert dezelfde interface | ``` lib/connectors/ ├── connector-interface.ts # EPDConnector interface ├── connector-registry.ts # Registry: welke connectors beschikbaar ├── connector-factory.ts # createConnector(config) → EPDConnector ├── types.ts # PatientSummary, ReportSummary, etc. └── implementations/ ├── own-epd-connector.ts # Directe Drizzle DB calls (standalone model) ├── hix-connector.ts # Chipsoft HiX via FHIR/HL7 API ├── fhir-connector.ts # Generiek FHIR R4 systeem └── mock-connector.ts # Voor tests en development ``` --- ## INFRASTRUCTUUR — DevOps & Deployment ### Docker Services | Service | Image | Poort | Doel | |---------|-------|-------|------| | `caddy` | `caddy:2-alpine` | 80, 443 | Reverse proxy, HTTPS | | `intent-platform` | Custom Dockerfile | 3001 | Intent Platform Next.js | | `epd-service` | Custom Dockerfile | 3000 | EPD Service Next.js | | `postgres` | `postgres:17-alpine` | 5432 | Databases (epd_db, platform_db, keycloak_db) | | `keycloak` | `quay.io/keycloak/keycloak:26.5` | 8080 | Auth server | | `valkey` | `valkey/valkey:8-alpine` | 6379 | BullMQ + caching (Redis-compatible) | | `postal` | `postalserver/postal:latest` | 25 | Email | ### PostgreSQL Extensions | Extensie | Doel | Component | |----------|------|-----------| | `pgvector` | Vector embeddings voor RAG | C12 | | `pg_trgm` | Trigram similarity search | C21, C36 | | `pgcrypto` | gen_random_uuid(), crypto functies | Alle tabellen | ### Hosting | Eigenschap | Waarde | |------------|--------| | **Provider** | Hetzner | | **Locatie** | Amsterdam (EU data residency) | | **Specs** | 4 vCPU, 8GB RAM, 160GB NVMe | | **OS** | Ubuntu 22.04 | | **Deployment** | Docker Compose + GitHub Actions (CI/CD) | | **Kosten** | €20-30/mnd | --- ## Volledige npm Dependencies ### EPD Service — `package.json` ```json { "dependencies": { "next": "^16.1.0", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwindcss": "^4.0.0", "drizzle-orm": "latest", "pg": "8.x", "zod": "^4.0.0", "zustand": "^5.0.0", "date-fns": "^4.0.0", "fuse.js": "latest", "lucide-react": "latest", "react-hook-form": "latest", "@hookform/resolvers": "latest", "next-auth": "5.x", "@auth/core": "latest", "ai": "latest", "@ai-sdk/anthropic": "latest", "bullmq": "5.x", "redis": "latest", "pino": "^10.0.0", "nodemailer": "latest", "@fullcalendar/react": "latest", "@fullcalendar/daygrid": "latest", "@fullcalendar/timegrid": "latest", "@opentelemetry/sdk-node": "latest", "@opentelemetry/api": "latest", "@bull-board/api": "latest", "@bull-board/express": "latest" }, "devDependencies": { "typescript": "5.x", "drizzle-kit": "latest", "@types/node": "latest", "@types/react": "latest", "@types/pg": "latest", "eslint": "latest", "vitest": "latest" } } ``` ### Intent Platform — `package.json` ```json { "dependencies": { "next": "^16.1.0", "react": "^19.0.0", "react-dom": "^19.0.0", "drizzle-orm": "latest", "pg": "8.x", "zod": "^4.0.0", "ai": "latest", "@ai-sdk/anthropic": "latest", "fuse.js": "latest", "next-auth": "5.x", "@auth/core": "latest", "bullmq": "5.x", "redis": "latest", "pino": "^10.0.0", "@opentelemetry/sdk-node": "latest", "@opentelemetry/api": "latest" }, "devDependencies": { "typescript": "5.x", "drizzle-kit": "latest", "@types/node": "latest", "@types/pg": "latest", "vitest": "latest" } } ``` --- ## Externe Services (betaald) | Service | Kosten | Doel | Component | |---------|--------|------|-----------| | Anthropic Claude API (via Vercel AI SDK) | ~$3/1M tokens | Intent classificatie, samenvattingen, chat | C07, C16 | | Deepgram API | ~$0.0043/min | Spraak-naar-tekst (Nederlands) | C03 | | Hetzner VPS | €15-25/mnd | Hosting | Infra | Alles overige is self-hosted en open source — geen verdere SaaS kosten. --- ## Samenvattingstabel — Alle 43 Componenten | # | Component | Laag | Technologie | Service | |---|-----------|------|-------------|---------| | C01 | Traditionele EPD UI | Frontend | Next.js, React, Tailwind, shadcn/ui | EPD | | C02 | Intent Command Center | Frontend | React, SSE | EPD + Platform | | C03 | Spraak Input | Frontend + API | Web Audio API, Deepgram | Platform | | C04 | Block/Artifact systeem | Frontend | React components | EPD | | C05 | Intent Registry | Backend | TypeScript config → DB | Platform | | C06 | Reflex Classifier | Backend | TypeScript, RegExp | Platform | | C07 | Orchestrator | Backend | Vercel AI SDK (@ai-sdk/anthropic), Zod | Platform | | C08 | Entity Extractor | Backend | TypeScript, Fuse.js | Platform | | C09 | Action System | Backend | TypeScript, Connector API | Platform | | C10 | Protocol Engine | Backend | TypeScript rule evaluator | Platform | | C11 | Protocol Rules Store | Backend (DB) | PostgreSQL, Drizzle | Platform | | C12 | RAG Pipeline | Backend | pgvector, MedRoBERTa.nl / Vercel AI SDK | Platform | | C13 | Regelvalidatie UI | Frontend | React, shadcn/ui DataTable | EPD (admin) | | C14 | Kennisbron Connectors | Backend | TypeScript HTTP clients | Platform | | C15 | Rapportage Service | Backend | Drizzle ORM | EPD | | C16 | Overdracht Service | Backend | Drizzle ORM, Vercel AI SDK | EPD | | C17 | Agenda Service | Backend | Drizzle ORM, FullCalendar (FE) | EPD | | C18 | Behandelplan Service | Backend | Drizzle ORM | EPD | | C19 | Medicatie Service | Backend | Drizzle ORM | EPD | | C20 | Intake Service | Backend | Drizzle ORM, Workflow Engine | EPD | | C21 | Patiënt Service | Backend | Drizzle ORM, Fuse.js, pg_trgm | EPD | | C22 | Toestemming Service | Backend | Drizzle ORM | EPD | | C23 | Auth + SSO | Middleware | Keycloak, Auth.js v5 | Beide | | C24 | Multi-tenancy | Middleware | PostgreSQL RLS, Drizzle context | Beide | | C25 | RBAC + ABAC Engine | Middleware | TypeScript policy evaluator | Beide | | C26 | Audit Trail | Middleware | PostgreSQL partitioned, crypto hash | Beide | | C27 | Field Encryption | Middleware | AES-256-GCM (node:crypto) | EPD | | C28 | FHIR Export | Middleware (API) | TypeScript mappers, FHIR R4 | EPD | | C29 | CDS Hooks | Middleware (API) | CDS Hooks 1.0 spec | Platform | | C30 | Observability | Middleware | Pino, OpenTelemetry | Beide | | C31 | Circuit Breaker | Middleware | TypeScript (~50 regels) | Platform | | C32 | Health Check | Middleware (API) | Next.js API route | Beide | | C33 | PII Filter | Middleware | TypeScript regex, Pino serializer | Platform | | C34 | Notificatie Service | Backend | SSE, BullMQ, Postal (SMTP) | EPD | | C35 | Workflow Engine | Backend | TypeScript state machine | EPD | | C36 | Full-Text Search | Backend (DB) | PostgreSQL tsvector (NL stemmer) | EPD | | C37 | Caching Layer | Backend | Valkey (node-redis) | Beide | | C38 | Scheduled Jobs | Backend | BullMQ repeatable jobs | Beide | | C39 | Webhook System | Backend | BullMQ, HMAC-SHA256 | EPD | | C40 | ZPM Facturatie | Backend | Drizzle ORM | EPD | | C41 | Vecozo Connector | Backend | Interface + mock (voorbereiding) | EPD | | C42 | Zorgdomein Connector | Backend | Interface + mock (voorbereiding) | EPD | | C43 | BI Module | Backend (DB) | PostgreSQL Materialized Views | EPD | --- ## Appendix: Relatie architectuurdocumenten ``` docs/architecture/ │ ├── intent-system-architectuur-nl.md │ → Hoe het intent systeem intern werkt (5 bouwblokken) │ ├── enterprise-epd-architectuur.md │ → Enterprise schil: multi-tenancy, audit, security, compliance │ ├── usecases-technische-componenten.md │ → 33 use cases × 43 componenten validatie + gap analyse │ ├── platform-connectiviteit-selfhosted.md │ → Self-hosted stack, platform splitsing, connector API │ ├── enterprise-componenten-uitbreiding.md │ → 10 nieuwe componenten (C34-C43): interfaces, schemas │ └── technische-invulling-componenten.md ← DIT DOCUMENT → Alle 43 componenten: frontend/backend/API/middleware indeling → Concrete technologiekeuzes, file structures, npm dependencies ```