1462 lines
51 KiB
Markdown
1462 lines
51 KiB
Markdown
# 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<Response>;
|
||
|
||
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
|
||
```
|