Ga naar inhoud

ADR-0004: Loose coupling — the core stays headless, experience/UI layers are separate consumers

Context

wordsworth keeps growing a constellation of surrounding components: production orchestration (NiFi, ADR-0001), source connectors (the Nextcloud connector, deliberately withdrawn from the core and kept as a standalone artifact that speaks only to the public /ingest), the interactive demo, a planned verifiable-credential reveal verifier (ADR-0003), and now a planned UI that develops ranking, anonymisation, pseudonymisation and key-sharing.

The recurring risk is that presentation-, source-, or orchestration-specific logic creeps into the engine. The Nextcloud episode is the cautionary tale: a source-specific connector was built inside wordsworth and had to be pulled back out. The explicit standing directive (Mark, 2026-08-27) is to guard that everything we build is microservice / loosely coupled.

Decision

wordsworth core is a headless engine with a stable public API — the linear straat (ingest → extract → pseudonymize → store → index → search → rank), grants, reveal, and export — with the append-only hash-chained audit as its only orchestration state. Nothing else lives inside it.

Everything else is a separate, independently-deployable consumer that talks to wordsworth only through its public API / contracts:

  • Orchestration → NiFi (ADR-0001), above wordsworth.
  • Source ingestion → connectors, each standalone, posting to /ingest (never importing from the package).
  • Reveal authorization → the EUDI/VC verifier (ADR-0003), opt-in, gating reveal in front of OpenBao.
  • Experience / UI → a separate frontend service (its own repo and deploy) that consumes the public API. Its modules — ranking, anonymisation & pseudonymisation views, key-sharing (grants) — are UI over the API, with no presentation logic in the core.

Rules that follow: 1. No source-specific, UI-specific, or orchestration-specific code in the core. 2. Each surrounding component is its own service/repo, independently deployable, testable, and replaceable. 3. The public API contract is the only coupling seam; components integrate through it, not through shared internals. 4. Graceful degradation (resilience invariant): any consumer — UI, connector, verifier, orchestrator — can fail without stopping the straat.

Consequences

  • Positive. Boundaries are explicit and enforceable; the core stays source- and UI-agnostic (the Nextcloud lesson, codified); every component is deployable and testable on its own; the planned UI cannot become a hidden dependency of the engine.
  • Cost. The public API becomes the contract that must stay stable and well-versioned; some duplication at the seam (each consumer maps the API to its own model) is accepted as the price of decoupling.
  • Neutral. This ADR codifies and generalises existing practice (ADR-0001, the Nextcloud withdrawal) into a governing principle; it does not change any current runtime behaviour.

The planned UI (scope note, not yet an implementation)

A separate frontend, API-first, consuming wordsworth's public endpoints. Candidate modules, each backed by an existing capability:

  • Ranking — search over the pseudonymised index; explain the score.
  • Anonymisation / pseudonymisation — show a document in index-form; make the pseudonyms legible (Persoon 1, Locatie 1, …).
  • Key-sharing — grants per department/role; issue, share, revoke; key-ring per type; every reveal audited without clear values.

The current interactive demo already prototypes ranking, pseudonymisation and key-sharing as a self-contained page; it is the seed, but the real UI is a separate service against the API — never folded into the core.