Files
register-referentie/docs/architecture/adr-0026-mutable-default-fill-store.md
not 0494730223
CI / build (push) Successful in 1m34s
CI / lint (push) Successful in 1m36s
CI / unit (push) Successful in 1m45s
CI / frontend (push) Successful in 3m37s
CI / mutation (push) Successful in 6m25s
CI / verify-stack (push) Successful in 7m56s
feat(portal-beheer): ACL default-fill configuration editor (closes #131) (#138)
## What & why

S-15b, second of the S-15 (#16) split, on top of S-15a (#133). A beheerder edits the ACL's ZGW **default-fill** values from the beheer portal, and the next zaak is stamped with the new values — no restart.

Closes #131

### The vertical

portal → BFF `GET/PUT /beheer/default-fill` (medewerker realm + `beheerder` role) → ACL `GET/PUT /default-fill` → a runtime-mutable in-memory store the ACL reads **per zaak**.

- **ACL**: `IDefaultFillStore` / `InMemoryDefaultFillStore` (thread-safe, seeded from `Acl:Defaults`); `AclService` reads `fill.Current` per zaak (not cached at construction); `GET`/`PUT /default-fill` with required-field validation.
- **BFF**: `IAclClient` gains `GetDefaultFillAsync`/`UpdateDefaultFillAsync`; `GET`/`PUT /beheer/default-fill` behind the `beheerder` policy. OpenAPI + generated client regenerated.
- **Frontend**: a *Default-fill* editor page in the beheer app (load → edit → save, with saved/failure states) + nav between Catalogus and Default-fill.

### Scope decision → ADR-0026

Only the **three ZGW fill fields** (bronorganisatie, verantwoordelijke organisatie, vertrouwelijkheidaanduiding) are editable. The S-27 catalog-resolution keys stay **static config** — editing them would desync the zaaktype-URL cache (ADR-0021), and they're catalogus wiring, not "default fill". The store is **in-memory** (seeded from config): an edit reverts on restart. That's the reference-app-appropriate ceiling (no DB added to the stateless ACL); upgrade path documented. Recorded in **ADR-0026**.

## Verified locally

lint (`dotnet format`) ✓ · .NET unit — acl 60 / bff 45 / domain 152 / event-subscriber 19 / acceptance 17 ✓ · frontend lint+test (8 projects) ✓ · beheer build ✓. Clean full-solution build (caught + fixed the acceptance `AclService` ctor drift). TDD red→green per layer (ACL store, ACL endpoints, BFF, frontend).

## Definition of Done

- [x] Failing test committed before each implementation (red→green per layer).
- [x] Conventional Commits referencing #131.
- [ ] CI green — see note below.
- [x] Docs: ADR-0026 + S-15b demo note.
- [x] Demo note in `docs/demo-script.md`.

## Note on CI

The bulk validates in the fast jobs (lint/build/unit/frontend/mutation). The **verify-stack e2e** (incl. the new `default-fill.spec.ts`) can't go green until the pre-existing **verify-stack bring-up failure on the 1.27/2.0.0 runner** is resolved (that fails on plain `main` too — unrelated to this PR). Additive change; no existing e2e touched.

🤖 Generated with [Claude Code](https://claude.com/claude-code)Reviewed-on: #138
2026-07-24 14:22:22 +00:00

2.7 KiB

ADR-0026: Runtime-mutable ACL default-fill (in-memory store, seeded from config)

  • Status: Accepted
  • Date: 2026-07-24
  • Deciders: Respellion engineering
  • Slice: S-15b (#131), second of the S-15 (#16) split

Context

ADR-0003 made the ACL default-fill the ZGW-mandatory fields it stamps on every zaak, supplied as static configuration (Acl:Defaults, read once at startup as an immutable singleton). S-15b lets a beheerder edit those values from the portal and have the next zaak reflect them — so the defaults must become mutable at runtime.

Two questions: what is editable, and where the mutable state lives.

Decision

Make the three ZGW default-fill fields a runtime-mutable, in-memory store (IDefaultFillStore), seeded from Acl:Defaults at startup. The ACL reads it per zaak; the beheer PUT /default-fill replaces it.

Only the three ZGW fill fields are editable

Acl:Defaults also carries the S-27 catalog-resolution keys (ZaaktypeIdentificatie, InformatieobjecttypeOmschrijving). Those feed the resolved-URL cache (CachedZaaktypeCatalog, ADR-0021); editing them at runtime would leave a stale cache and is catalogus wiring, not "default fill". So they stay static config and are out of scope for the CRUD. The editable set is exactly Bronorganisatie, VerantwoordelijkeOrganisatie, Vertrouwelijkheidaanduiding (DefaultFillSettings).

In-memory, not persisted

The store is a thread-safe in-memory singleton. An edit is lost on restart, when it reverts to the configured env. That is acceptable for this reference app: the slice demonstrates the pattern (beheer edits config that the ACL honours), not durable config management. The ACL stays stateless — no DB, no EF, no migration, no extra compose service.

  • ponytail ceiling: no persistence, no audit trail, no optimistic concurrency.
  • Upgrade path: back IDefaultFillStore with a DB (or an Objecten record) if durable, audited, multi-instance config is needed — the port stays the same.

Consequences

Positive

  • Demoable end to end (edit in portal → next zaak reflects it) with minimal moving parts.
  • The read path is per-zaak, so no restart and no cache concerns for the ZGW fields.

Negative / costs

  • Edits don't survive a restart and aren't shared across replicas (single-instance assumption). Documented ceiling above.
  • Two sources of default config now (static keys on AclDefaults, mutable fields in the store) — a deliberate split by editability.

Coupling rules touched (CLAUDE.md §8)

None new. The BFF→ACL edge already exists (ADR-0025); this adds a read/write pair on it. The ACL remains the owner of the ZGW-facing config.