# 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
```
