Files
register-referentie/docs/PRD.md
Edwin van den Houdt 1913aa5628 docs(prd): target .NET 10 LTS instead of 9 (refs #28)
The PRD pinned ".NET 9 (LTS at iteration time)", but .NET 9 is STS and
reaches end-of-life around now; the actual LTS at this iteration is .NET
10. The BFF (and the services to follow) target net10.0, pinned via
global.json. Correct the PRD to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 13:36:15 +02:00

16 KiB
Raw Blame History

BIG-Register Reference Application — PRD

Status: v1 draft
Owner: Respellion
Primary remote: Gitea (self-hosted, Respellion)
Purpose: A reference application that demonstrates Respellion's Common Ground architecture pattern — using OpenZaak and other Common Ground modules behind a loose-coupling anti-corruption layer, with workflow, scheduling, and data governance as peer modules. It serves as a Foundations playbook artefact and as a credible, runnable demonstrator for Dutch public-sector engagements.


1. Goals

  1. Demonstrate that a domain-specific case management application (a BIG-style register) can be built on top of unforked Common Ground modules by isolating all domain shaping in an Anti-Corruption Layer.
  2. Show four end-user portals (self-service, public register, back-office, maintenance) sharing one domain layer through a BFF.
  3. Show process orchestration via Flowable as a peer module, with BPMN + DMN governing the registration workflow.
  4. Show data governance and GDPR lineage as a separate, integratable module (later iteration).
  5. Be fully runnable locally with docker compose up, including all upstream Common Ground modules, identity, workflow engine, and synthetic data.
  6. Encode Respellion craft defaults: TDD, DDD, BDD, mutation testing, trunk-based development, Conventional Commits, ADRs, and CI gates.
  7. Run end-to-end on Gitea infrastructure — source, issues, CI, container registry, releases, wiki — to demonstrate EU-sovereign tooling.

2. Non-goals

  • Not a certified production system. No real DigiD/eHerkenning/eIDAS — mocked via Keycloak.
  • Not a multi-tenant SaaS. One instance, one client persona, per deployment.
  • Not feature-complete to the real Dutch BIG-register. Modelled as a plausible analogue, not a copy.
  • Not optimised for production scale; correctness and architectural clarity beat throughput.
  • Not dependent on GitHub. The repository lives on a Respellion Gitea instance; GitHub mirroring is explicitly out of scope unless added later as an outreach measure.

3. Architecture summary

Refer to docs/architecture/ for diagrams. Briefly:

  • Portals (Angular, NL Design System): self-service, openbaar register, behandel-portal, beheer-portal — four apps in an Nx monorepo with a shared component library.
  • BFF (.NET): per-portal aggregation, OIDC token validation, audit, rate-limiting.
  • BIG Domain Service (.NET): DDD aggregates, registration workflow, eligibility rules, orchestrates ACL and workflow engine.
  • Anti-Corruption Layer (.NET): the only code that knows about ZGW APIs. Default-fills schema-mandatory fields, maps domain ↔ ZGW vocabulary.
  • Workflow Client (.NET): REST client to Flowable; acts as external-task job worker so BPMN never reaches into OpenZaak.
  • Event Subscriber (.NET): consumes NRC webhooks, updates the read projection, correlates events to running Flowable processes.
  • Read Projection Store: denormalised, public-safe view feeding the openbaar register portal.
  • Scheduler (Quartz.NET): fleet-wide sweeps (expiry, reminders).
  • Common Ground modules (upstream, unforked): OpenZaak, Open Notificaties, Open Klant, Objecten + Objecttypen, Autorisaties.
  • Flowable: BPMN + DMN engines with Postgres-backed jobs.
  • Identity: Keycloak realms standing in for DigiD, eHerkenning, eIDAS, and a medewerker-IdP.
  • Data Governance (later): OpenMetadata as a separate module, ingesting from read replicas and via a lineage SDK.

4. User types and primary scenarios

User type Auth Portal Primary scenario
Zorgprofessional DigiD (mock) Self-Service Submit registration, upload diploma, track status
Werkgever / Onderwijsinstelling eHerkenning (mock) Self-Service Register on behalf of a professional (machtiging)
EU-professional eIDAS (mock) Self-Service Foreign diploma registration with extra checks
Burger / Werkgever-check anonymous Openbaar Register Lookup a professional's registration status
Behandelaar / Jurist medewerker-IdP Behandel-Portal Review cases, request additional documents, decide
Beheerder medewerker-IdP + MFA Beheer-Portal Manage catalogus, configure default-fill rules, monitor

5. Functional scope — flows for v1

  1. Happy path registration — DigiD login, submit, upload diploma, automatic verification passes, behandelaar approves, register entry published.
  2. Document timeout — registration submitted, no diploma uploaded within 30 days, automatic cancellation.
  3. Withdrawal — applicant withdraws before decision; case cancelled with audit trail.
  4. Foreign diploma escalation — eIDAS login, diploma flagged as foreign, additional CBGV-style assessment step.
  5. Beoordeling escalation — behandelaar does not pick up within 14 days, case escalates to teamlead.

The five flows form the BDD acceptance backbone (Gherkin scenarios in tests/acceptance/).

6. Synthetic data

  • Fixed seed loaded on first start: 510 hand-crafted personas covering all five flows, plus ~50 register entries with diverse statuses to populate the openbaar register.
  • Dutch-realistic names; valid test-BSN range documented in docs/synthetic-data.md.
  • Diplomas: PDF placeholders with deterministic content, generated by a small data-loader service on first compose-up.
  • Re-seeding: a make seed / pwsh ./tools/seed.ps1 target rebuilds from scratch.

7. Non-functional requirements

  • Local startup: docker compose up brings everything green within 3 minutes on a developer machine.
  • CI gates (Gitea Actions): lint, unit, integration, mutation (with ratchet), e2e for the current flow, container build pushed to Gitea Container Registry, compose-up smoke test.
  • TDD discipline: red → green → refactor commits visible in history.
  • Mutation testing: Stryker.NET (backend) and Stryker (Angular), baseline-ratcheted on main.
  • Coverage: not a target; mutation score is the quality signal.
  • Observability: structured logs (Serilog), OpenTelemetry traces, Prometheus metrics; a local Grafana with pre-built dashboards.
  • Security: all secrets in Gitea repository/organisation secrets or local .env (gitignored); no secrets in repo. OWASP dependency scanning step in the Gitea Actions pipeline.
  • Conventional Commits + git-cliff generated changelog. CalVer versioning.

8. Technology choices (locked)

  • Source control & collaboration: Gitea (Respellion self-hosted) — repository, issues, milestones, labels, projects, releases, container registry, wiki, packages.
  • CI/CD: Gitea Actions running on Respellion-hosted act_runner instances. Workflow files live in .gitea/workflows/. Marketplace actions are referenced via absolute URLs (uses: https://github.com/actions/checkout@v4 or Gitea-hosted equivalents where available) for reproducibility.
  • Backend: .NET 10 (LTS at iteration time), C#, minimal APIs for BFF, MediatR for in-process messaging within Domain Service, EF Core for the projection store and domain DB.
  • Frontend: Angular (latest LTS) + TypeScript, standalone components + signals, Nx monorepo, NL Design System component library, Angular Testing Library + Playwright.
  • Workflow: Flowable (BPMN + DMN) via Docker image; Postgres for engine store.
  • Identity: Keycloak with pre-seeded realms.
  • Common Ground: official upstream Docker images for OpenZaak, Open Notificaties, Open Klant, Objecten, Objecttypen, Autorisaties.
  • Datastore: Postgres for all .NET services; one schema per service.
  • Scheduler: Quartz.NET inside the Domain Service deployment.
  • Container registry: Gitea's built-in container registry; image references in compose pin to gitea.respellion.local/big-reference/<service>:<tag>.
  • Containers: Docker for all components; compose for local; Helm chart sketch for production aspirations (not deployed as part of v1).
  • Docs: MkDocs Material plus Gitea's built-in wiki for living operational pages. See §10.
  • Languages of artefacts: code, commits, ADRs, docs in English; user-facing portal copy and glossary terms in Dutch.

9. Repository structure (high-level)

/
├── apps/                       # Angular portals (Nx)
│   ├── self-service/
│   ├── openbaar/
│   ├── behandel/
│   └── beheer/
├── libs/                       # shared Angular libs (UI, auth, api-client)
├── services/                   # .NET services
│   ├── bff/
│   ├── domain/                 # BIG Domain Service
│   ├── acl/                    # standalone ACL library + service
│   ├── event-subscriber/
│   └── projection-api/
├── workflows/                  # BPMN + DMN sources, exported and version-controlled
├── infra/
│   ├── docker-compose.yml
│   ├── keycloak/
│   ├── opennotificaties/
│   ├── openzaak/
│   ├── objecten/
│   ├── flowable/
│   └── seed/                   # data loader
├── tests/
│   ├── acceptance/             # Gherkin / Reqnroll BDD scenarios
│   └── e2e/                    # Playwright
├── docs/                       # static docs (MkDocs source)
│   ├── PRD.md
│   ├── architecture/           # diagrams + ADRs
│   ├── runbooks/
│   ├── synthetic-data.md
│   └── gitea-workflow.md       # how we use Gitea for issues/milestones/projects
├── .gitea/
│   ├── workflows/              # Gitea Actions YAML
│   └── ISSUE_TEMPLATE/         # issue templates (slice, bug, ADR-proposal)
├── CLAUDE.md                   # working agreements
├── BACKLOG.md                  # pointer + mirror of the active Gitea milestone backlog
└── README.md

10. Documentation strategy — split between static and living docs

Two surfaces, each with a clear role:

Static, versioned documentation — docs/ rendered by MkDocs Material.

  • The PRD, architecture diagrams (Mermaid sources in repo), ADRs, glossary, synthetic-data spec, and runbooks live here.
  • Source of truth, versioned with the code, reviewed via PR.
  • Published artefact: an MkDocs site built by a Gitea Actions workflow on every push to main and exposed via Gitea Pages (or a static-files job uploading to a Respellion-hosted location).
  • ADRs use the Nygard template, one decision per file (adr-NNNN-title.md). First ADR (adr-0001-loose-coupling.md) is provided as a template.

Living, operational documentation — Gitea Wiki.

  • The Gitea project's built-in Wiki holds operational pages that change frequently and where in-place editing matters: the day-to-day runbook, "known issues right now," the on-call rota, environment URLs, and the human-readable view of the iteration backlog.
  • The Wiki is not the source of truth for architecture or ADRs — those stay in docs/. The Wiki points at them.
  • The Wiki is part of the deliverable and is reviewed alongside the code.

Why split: versioned docs benefit from PR review and ship with the code; operational pages benefit from in-UI editing and don't need a full PR cycle. Treating both as legitimate gives each its proper home.

11. Story and backlog management — Gitea is the system of record

User stories, defects, technical debt, and ADR proposals are tracked in Gitea Issues. The BACKLOG.md file in the repo is a curated mirror of the current milestone — it exists so that Claude Code and contributors can read the iteration plan without opening the web UI, but the system of record is Gitea.

Concrete conventions:

  • Each slice in BACKLOG.md corresponds 1:1 to a Gitea issue with the same S-NN identifier in the title (e.g. S-04 · ACL skeleton with one operation: open a zaak).
  • Milestones group slices into iterations: Iteration 0 — Foundations, Iteration 1 — Walking Skeleton, etc.
  • Labels classify work: type:slice, type:bug, type:adr-proposal, type:chore, plus area labels (area:acl, area:domain, area:portal-self-service, area:infra, area:docs).
  • Projects (Gitea's kanban-style project boards) visualise an iteration as a board: Todo / In progress / In review / Done.
  • Issue templates live in .gitea/ISSUE_TEMPLATE/ with three forms: slice.md, bug.md, adr-proposal.md. The slice template encodes the Definition of Done from CLAUDE.md §3 as a checklist.
  • Commits and PRs reference issues with #NN (e.g. feat(acl): default-fill bronorganisatie (refs #14)). Closing keywords on the merging commit (closes #14) move the issue to Done automatically.
  • Pull Requests are the unit of code review. Trunk-based, squash-merged, with the PR description summarising the slice's acceptance.
  • Releases are cut on tags via git-cliff-generated changelogs and published to Gitea Releases. Container images are tagged matching the release.

BACKLOG.md is regenerated (manually or by a small script) whenever a milestone is opened or closed. Treat it as a snapshot, not the source of truth.

12. Iteration shape — walking skeleton first

The first iteration is a walking skeleton that proves the spine end-to-end with minimal features. Each subsequent iteration is a vertical slice that adds one capability, deployable on its own. See BACKLOG.md for the slice list.

The skeleton: zorgprofessional logs in via mock DigiD → self-service portal submits a registration → BFF calls Domain Service → Domain Service starts a Flowable BPMN process and opens a zaak via the ACL → status event flows via NRC to the Event Subscriber → projection updated → openbaar register portal shows the entry.

Everything in the skeleton is real (no mocks beyond the IdP). Subsequent slices flesh out scenarios, edge cases, and the remaining portals/modules.

13. Definition of Done (per slice)

A slice is done when:

  • Red → green → refactor commits visible in history (Conventional Commits).
  • All CI gates pass on Gitea Actions (lint, unit, integration, mutation baseline, e2e for the slice's scenario, container build + push to Gitea Container Registry, compose-up smoke test).
  • docker compose up from a fresh clone yields green health checks within 3 minutes.
  • The Gherkin scenario(s) that define the slice pass.
  • An ADR is added in docs/architecture/ if a non-obvious decision was made.
  • The relevant runbook section is updated (in docs/runbooks/ or the Wiki, as appropriate per §10).
  • A short demo note appended to docs/demo-script.md.
  • The corresponding Gitea issue is closed via the merging commit's footer (closes #NN).

14. Open risks tracked

  • OpenZaak version drift. Pin upstream image tags; document the upgrade procedure.
  • Flowable + .NET integration friction. External-task worker pattern via REST; mitigate by isolating in Workflow Client.
  • NL Design System completeness. Some components may need polyfills; record gaps in docs/frontend-decisions.md.
  • NRC ordering and duplication. Event Subscriber must be idempotent; verified by acceptance tests that replay events.
  • Gitea Actions ↔ GitHub Marketplace action drift. Some marketplace actions assume GitHub-specific APIs. Workflows pin actions to known-good versions; gaps are recorded in docs/runbooks/gitea-actions-gotchas.md.

15. Out of scope for v1

  • OpenMetadata data governance module (v3 slice).
  • Objecten as the authoritative register record store (v2 slice — v1 uses OpenZaak zaak-eigenschappen as a placeholder).
  • Production-grade Helm chart (sketch only).
  • Multi-tenancy.
  • Real outbound notifications (email/SMS) — logged to console in v1.
  • GitHub mirroring or any GitHub-hosted automation.