# register-reference A reference application demonstrating how to build a Dutch government **register** (a BIG-style professional register) on top of unforked **Common Ground** modules — OpenZaak, Open Notificaties, Objecten, Open Klant — with loose coupling, modern workflow tooling, and data-governance ready integration points. This repository is the runnable companion to Respellion's Foundations playbook entry on Common Ground architecture for non-municipal contexts (CIBG, DUO, RVO, and similar uitvoeringsorganisaties). > **Status:** under active development. See [BACKLOG.md](BACKLOG.md) for the current iteration. --- ## Start here | Document | Purpose | |---|---| | **[docs/PRD.md](docs/PRD.md)** | What we're building and why. Goals, non-goals, architecture summary, scope. Read once at project start. | | **[CLAUDE.md](CLAUDE.md)** | How we work. Engineering principles, TDD/DDD/BDD discipline, non-negotiable architectural rules, Gitea conventions. Read every task. | | **[BACKLOG.md](BACKLOG.md)** | Iteration plan. A curated mirror of the active Gitea milestone — Gitea Issues are the system of record. | | **[docs/architecture/](docs/architecture/)** | Diagrams (Mermaid sources) and ADRs. Start with `adr-0001-loose-coupling.md`. | | **[docs/runbooks/](docs/runbooks/)** | Operational guides: local startup, seeding, common failures, CI debugging. | The day-to-day operational pages — environment URLs, known issues right now, on-call notes — live in the **Gitea Wiki** for this repository. The wiki points at `docs/` for anything authoritative. --- ## What this application demonstrates - A **BIG-style professional register** modelled on Dutch public-sector patterns, with four end-user portals: self-service, openbaar register, behandel-portal, beheer-portal. - **Common Ground modules as upstream peers** — never forked, reached only via documented APIs (ZGW, NRC events). - **An Anti-Corruption Layer** that confines all ZGW knowledge to one place, so the rest of the codebase stays domain-shaped rather than municipality-shaped. - **BPMN + DMN workflows via Flowable** as a separate, swappable module — using the external-task job-worker pattern so BPMN models never reach into OpenZaak. - **A read projection** as the public-facing data path, decoupled from the authoritative modules. - **Synthetic data and mock identity** (Keycloak realms standing in for DigiD, eHerkenning, eIDAS, and a medewerker IdP) so the whole system runs locally without external dependencies. - **TDD, DDD, BDD, mutation testing, ADRs, and Conventional Commits** as enforced defaults — encoded in CI. - **Gitea-native delivery**: source, issues, milestones, project boards, releases, container registry, packages, wiki, and Actions. For the architecture rationale, see [docs/PRD.md §3](docs/PRD.md) and [docs/architecture/](docs/architecture/). --- ## Local quickstart **Prerequisites** - .NET 10 SDK (for `make lint/build/unit`) - A container engine with Compose v2 — Docker, or rootless Podman (see [docs/runbooks/ci.md](docs/runbooks/ci.md) for the Podman + Compose-provider setup) - `make`, `curl`, `git` - ~4 GB free RAM, ~5 GB free disk (grows as services land) **Clone** ```bash git clone git@git.labs.respellion.tech:eho/register-referentie.git cd register-referentie ``` **Wired today (Iteration 0):** only the placeholder BFF exists so far. Get to green in under 10 minutes — run the full check gate, or just the running service: ```bash make ci # lint + build + unit + container smoke — the CI gate ``` ```bash docker compose -f infra/docker-compose.yml up -d --build --wait curl http://localhost:8080/health # -> Healthy ``` `--wait` exits non-zero unless the container reports healthy, so it doubles as the compose-up smoke test. The remaining services and the URLs below land in later slices. **Target service URLs** *(most land in later slices)* | Service | URL | |---|---| | Self-Service portal | http://localhost:4200 | | Openbaar register | http://localhost:4201 | | Behandel-portal | http://localhost:4202 | | Beheer-portal | http://localhost:4203 | | BFF | http://localhost:8080 | | OpenZaak | http://localhost:8000 | | Open Notificaties | http://localhost:8001 | | Flowable | http://localhost:8080 | | Keycloak | http://localhost:8180 | | MkDocs site (after build) | http://localhost:8000/docs/ | Test credentials, BSNs, and personas: see [docs/synthetic-data.md](docs/synthetic-data.md). **Build the docs site** ```bash python3 -m venv .venv && .venv/bin/pip install mkdocs-material .venv/bin/mkdocs serve # live preview at http://localhost:8000 .venv/bin/mkdocs build # static site in ./site ``` --- ## Repository layout ``` register-reference/ ├── apps/ # Angular portals (Nx monorepo) │ ├── self-service/ │ ├── openbaar/ │ ├── behandel/ │ └── beheer/ ├── libs/ # shared Angular libs (UI, auth, generated API client) ├── services/ # .NET services │ ├── bff/ │ ├── domain/ # BIG Domain Service │ ├── acl/ # Anti-Corruption Layer (the only code that knows ZGW) │ ├── event-subscriber/ │ └── projection-api/ ├── workflows/ # BPMN + DMN sources ├── infra/ # docker-compose, Keycloak, OpenZaak, Flowable, seed ├── tests/ │ ├── acceptance/ # Gherkin / Reqnroll BDD scenarios │ └── e2e/ # Playwright ├── docs/ # versioned documentation (MkDocs source) ├── .gitea/ # Gitea Actions workflows, issue/PR templates ├── CLAUDE.md # working agreements ├── BACKLOG.md # iteration plan (mirror of active milestone) └── README.md ``` Full description in [docs/PRD.md §9](docs/PRD.md). --- ## Working in this repository **Source of truth for work:** Gitea Issues + Milestones for this repository. `BACKLOG.md` is a mirror. **Branching:** trunk-based. Short-lived branches off `main`, named `/-` (e.g. `feat/14-acl-default-fill`). **Commits:** Conventional Commits, referencing the Gitea issue: ``` feat(acl): default-fill bronorganisatie (refs #14) ``` The merging PR closes the issue via `closes #14` in the squash-commit body. **Pull Requests:** the unit of review. Squash-merged. PR template enforces the Definition of Done checklist from [CLAUDE.md §3](CLAUDE.md). **Releases:** CalVer (`YYYY.MM.PATCH`), tagged on `main`, changelog generated by `git-cliff`, published as a Gitea Release with container images in the Gitea Container Registry. See [CLAUDE.md](CLAUDE.md) for the full working agreements, the architectural non-negotiables, and the rules Claude Code follows on every task. --- ## Testing - **Unit tests** — dominant. .NET (xUnit) and Angular (Vitest / Testing Library). - **Integration tests** — Testcontainers-driven, exercising real OpenZaak, Flowable, NRC. - **Acceptance tests** — Gherkin scenarios in `tests/acceptance/`, one per user-visible flow. - **End-to-end** — Playwright, expanding slice by slice from the walking-skeleton happy path. - **Mutation testing** — Stryker.NET and Stryker, baseline-ratcheted on `main`. Run everything: ```bash ./tools/test-all.sh ``` Run a focused slice (example): ```bash dotnet test services/acl ``` --- ## Contributing 1. Find or open a Gitea issue using one of the templates in `.gitea/ISSUE_TEMPLATE/` (`slice.md`, `bug.md`, `adr-proposal.md`). 2. Assign yourself, move it to "In progress" on the milestone's project board. 3. Branch off `main`, follow the [CLAUDE.md](CLAUDE.md) working agreements (TDD: red commit → green commit → refactor commit). 4. Open a PR using the template, link the issue, ensure the Gitea Actions pipeline is green. 5. Squash-merge once approved. The merging commit closes the issue. If a task pushes against any of the architectural rules in [CLAUDE.md §8](CLAUDE.md), **stop and open an `adr-proposal` issue first.** That conversation is more important than the code. --- ## License and attribution Respellion-authored code is licensed under [EUPL-1.2](LICENSE). Upstream Common Ground modules retain their own licences (typically EUPL-1.2 or MIT — see each module's repository). This reference application is **not** an official product of CIBG, DUO, VNG Realisatie, or any government body. It is a Respellion playbook artefact illustrating an architectural pattern. --- ## Contact - Issues, questions, proposals: open a Gitea issue on this repository. - Architectural discussion: start with an `adr-proposal` issue. - Anything sensitive: contact Respellion through the channel in `docs/runbooks/contact.md`.