215 lines
16 KiB
Markdown
215 lines
16 KiB
Markdown
# 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: 5–10 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 9 (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.
|