Files
register-referentie/docs/architecture/adr-0011-approval-status-flow.md
Niek Otten 8badef02af
Some checks failed
CI / lint (pull_request) Successful in 1m7s
CI / build (pull_request) Successful in 54s
CI / unit (pull_request) Successful in 1m4s
CI / frontend (pull_request) Successful in 2m0s
CI / mutation (pull_request) Failing after 1m54s
CI / verify-stack (pull_request) Failing after 5m49s
docs(architecture): ADR-0011 approval status flow + demo note (refs #75)
Records the two non-obvious decisions: the ACL resolves the zaaktype eindstatus
(domain stays ZGW-ignorant), and the event-subscriber projects INGESCHREVEN from
the notification alone (hoofdObject + any status-create), never reading OpenZaak.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 17:05:12 +02:00

4.2 KiB

ADR-0011: Approval sets the zaak eindstatus via the ACL and projects INGESCHREVEN from the notification alone

  • Status: Accepted
  • Date: 2026-07-13
  • Deciders: Respellion engineering
  • Relates to: S-09b (#75); split from S-09 (#10); builds on ADR-0001 (§8 loose coupling), ADR-0003 (ACL default-fill), ADR-0007 (OZ→NRC wiring), ADR-0008 (read projection), ADR-0009 (external-task worker)

Context

The walking skeleton could submit a registration (INGEDIEND) and show it in the openbaar register, but nothing could approve it. S-09b adds a behandelaar approval that must make the entry publicly visible as a terminal status. There is no behandel-portal yet (S-12), so approval is triggered by a temporary admin endpoint on the Domain Service.

Two decisions are non-obvious (§14) and cross service boundaries:

  1. Who resolves the ZGW statustype? Approval means "set the zaak to its final status", but the domain must stay ZGW-ignorant (§8.1 — only the ACL talks to ZGW) and does not know statustype URLs.
  2. How does the projection learn the new status? The status is set in OpenZaak, which notifies over NRC; the Event Subscriber projects it. But the subscriber may not read OpenZaak (§8.1), and an NRC status/create notification's resourceUrl is the status resource, not the zaak, and does not carry the statustype.

Decision

Approval flows Domain → ACL → OpenZaak → NRC → Event Subscriber → projection, using only the notification's own fields on the read side.

  • Domain. Registration.Approve() advances INGEDIEND → INGESCHREVEN (requires an opened zaak; a repeat is a no-op). The ApproveRegistration use case calls the ACL to set the zaak status, then advances the aggregate. A temporary POST /registrations/{id}/approve endpoint drives it.
  • ACL. A new POST /statussen operation takes only the zaak URL. The ACL resolves the zaaktype's eindstatus from the catalogus (isEindstatus, falling back to the highest volgnummer) and POSTs a ZGW status against the zaak. The domain never names statustypen — the ACL owns the ZGW translation (§8.1, ADR-0003).
  • Event Subscriber. It binds the NRC hoofdObject (always the zaak URL) and keys the projection on it, so a zaken/status/create notification updates the same row the zaak-create created, flipping it to INGESCHREVEN. It takes any status-create as the approval — in the walking skeleton the only status ever set after creation is the approval — so it never has to read OpenZaak to learn the statustype. The ZGW resource is retained in the notification log (new column) so a rebuild reproduces the right status.

Consequences

  • The domain↔ACL boundary stays clean: the domain hands over a zaak URL and says "approve"; ZGW statustype knowledge lives only in the ACL.
  • The projection remains rebuildable without OpenZaak (§8.1, ADR-0008): the log now records the ZGW resource, which is all a rebuild needs to reproject the status.
  • The openbaar register shows real lifecycle: INGEDIEND on submit, INGESCHREVEN on approval.
  • Walking-skeleton assumption: "any status-create ⇒ INGESCHREVEN" holds only while approval is the sole post-creation status transition. When more transitions arrive (beoordeling, afwijzing — S-12+), the subscriber must distinguish statustypen. The honest options then are to carry the statustype omschrijving in the notification kenmerken, or to have the ACL resolve it and re-notify — recorded here so future-me revisits this rather than assuming it generalises.

Alternatives considered

  • Inject the approved statustype URL into the ACL as config (like the zaaktype URL). Rejected: couples ACL config to seed output and adds compose/run-domain-check plumbing; runtime eindstatus discovery keeps the ACL self-contained for one extra ZGW GET per approval.
  • Have the Event Subscriber GET the status/statustype from OpenZaak to map precisely. Rejected: violates §8.1 (only the ACL talks to ZGW) and makes the projection depend on OpenZaak being up.
  • Record the derived status in the notification log instead of the ZGW resource. Rejected: the log should retain notification facts, not projection semantics; the mapping stays in the projector.