Files
triqura-ecd/docs/architecture/technische-invulling-componenten.md
2026-07-09 23:16:28 +02:00

51 KiB
Raw Permalink Blame History

Technische Invulling — Alle Componenten (C01C43)

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)

// 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)

// 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:

// 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 250ms3s
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 dependenciesnode: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

{
  "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

{
  "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