Two work packages in one commit because both edit Program.cs and splitting
them would leave a commit that does not build.
WP-72 — deletes POST /api/v1/intakes and /herregistraties. Both were dead
from the UI (the wizard submits via /applications/{id}/submit) and strictly
less capable: they minted a bare reference and wrote no Aanvraag, made no
ZGW call, and did no document-ownership check. The shared Submit(...) helper
survives — /registrations and /change-requests still use it. WP-69 hardened
/intakes with a 400 last session; removing the surface is the stronger fix,
and WP-69's /applications/{id}/submit enforcement is untouched.
WP-73 — RegistrationStatus becomes an abstract record with three sealed
variants behind a private base ctor, so only Geregistreerd carries a
herregistratie deadline and reden is required on Geschorst/Doorgehaald
(matching the FE union, which was already right). HerregistratieRule
.IsStatusConsistent and its test are deleted: the type now guarantees what
the runtime check was for, and the test could no longer construct the
illegal state it existed to catch.
Aanvraag splits into a Concept | Submitted | Decided union with the EF row
demoted to AanvraagEntity behind a two-way mapper. Submitted carries a
non-null Referentie and SubmittedAt, and Decided.Afgewezen/MeerInfoGevraagd
require a Toelichting — so the five Referentie! null-forgiving derefs in
StatusAt are gone, not merely suppressed. IZaakSource.CreateZaak narrows to
Aanvraag.Submitted, removing the same class of deref in both zaak sources.
Draft is now cleared on submit rather than lingering: ApplicationStore's
doc-comment claimed "Concept only" but Submit never cleared it. Verified
nothing reads a submitted aanvraag's draft (draft-sync's applyResume only
resumes unsubmitted wizards), so the comment is now true instead of
aspirational.
No migration, no schema change, no wire change — RegistrationStatusDto and
the application DTOs are byte-identical, confirmed against a live swagger.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
4.6 KiB
BIG-register BFF (ASP.NET Core)
The backend that hosts the business rules for the BIG-register portal. The
frontend renders the decisions this service computes; it does not recompute them
(BFF-lite + decision DTOs — see ../docs/reference/architecture/0001-bff-lite-decision-dtos.md).
No real BRP/DUO: the reference data they'd return (registration, person, diplomas,
notes — Data/SeedData.cs) is in-memory and seeded, but the endpoints, DTOs,
status codes and error envelope are production-shaped.
Applications, documents and the brief persist to a SQLite file
(src/BigRegister.Api/bigregister.db, EF Core-backed — Data/AppDbContext.cs,
Data/Db.cs) created and migrated on first run; restarting the process (or
docker compose restart api — the existing ./backend:/src bind mount already
covers it, see docker-compose.yml) does not lose data. Delete the file to
reset demo data back to empty, the same state a fresh clone starts from. This is
a deliberate, right-sized choice for a POC (SQLite, no external DB service) — see
docs/project/backlog/WP-22-durable-persistence.md.
Run
Everything (docker-compose, from repo root)
docker compose up
- App: http://localhost:4200
- Swagger UI: http://localhost:5000/swagger
Backend only (local)
cd backend
dotnet run --project src/BigRegister.Api
# → http://localhost:5000/swagger
Frontend against a local backend
npm start # ng serve, proxies /api → http://localhost:5000 (proxy.conf.json)
Tests
cd backend && dotnet test # rule unit tests + endpoint integration tests
API
| Method | Route | Purpose |
|---|---|---|
| GET | /api/dashboard-view |
registration + person + computed herregistratie decision |
| GET | /api/notes |
specialisms / aantekeningen |
| GET | /api/brp/address |
BRP address lookup (gevonden:false = no address) |
| GET | /api/duo/diplomas |
diplomas with derived profession + applicable policy questions, + manual fallback |
| GET | /api/intake/policy |
scholing threshold (config value) |
| POST | /api/registrations |
submit registration → reference, or 422 (manual diploma) |
Rejections use ProblemDetails (RFC 7807) with status 422. Every request
carries an X-Correlation-Id (set by the FE fetch adapter); the backend echoes it
into a no-PII submit-audit log line (kind, outcome, reference, correlation id)
— the seam for real structured logging / an audit store.
Versioning
Endpoints live under /api/v1. Additive changes (a new optional field) stay on
v1: the NSwag-generated client and the FE parse* boundary ignore unknown fields,
so old clients keep working. A breaking change (renamed/removed field, changed
semantics) is introduced as /api/v2 served alongside v1 until clients migrate.
Where the rules live (src/BigRegister.Api/Domain/)
Diplomas/DiplomaRules.cs— profession derivation + which policy questions apply.Registrations/HerregistratieRule.cs— eligibility + reason + status invariant.Intake/IntakePolicy.cs— scholing threshold + completeness re-validation on submit (RejectIncompleteScholing, WP-69).Submissions/SubmissionRules.cs— submit rejections + reference generation.
Typed client (NSwag)
The frontend calls this API through a generated TypeScript client. Regenerate it from the contract after a shape change:
npm run gen:api # builds backend → swagger.json → src/app/shared/infrastructure/api-client.ts
Maintainability: changing a policy is one backend change
Goal: require every Verpleegkundige diploma to confirm a Dutch skills assessment. This is a new policy question on a diploma type.
Edit one file — Domain/Diplomas/DiplomaRules.cs:
public static IReadOnlyList<PolicyQuestion> QuestionsFor(Diploma d)
{
var questions = new List<PolicyQuestion>();
if (d.Engelstalig)
questions.Add(NlTaalEngelstalig);
+ if (d.Opleiding == "verpleegkunde")
+ questions.Add(new PolicyQuestion(
+ "bekwaamheid",
+ "Heeft u in de afgelopen vijf jaar een bekwaamheidstoets afgelegd?",
+ QuestionType.JaNee));
return questions;
}
Rebuild the backend (docker compose up or dotnet run). The new question now
appears in the registration wizard for HBO-Verpleegkunde.
- No frontend change. The FE renders whatever questions the API returns.
- No client regeneration. The wire shape (
PolicyQuestionDto) is unchanged — only the data behind it.npm run gen:apiis only needed when a DTO shape changes.
Add a unit test for the new rule in tests/BigRegister.Tests/RuleTests.cs and
you're done.