Files
atomic-design-poc/docs/project/archive/backlog/WP-49-openzaak-zaken-read-seam.md
ehoandClaude Opus 5 12f17d9d73 docs: archive the finished backlogs (RD-30)
Two backlog trees are complete: `docs/project/backlog/` (75 files, every
WP done) and `docs/project/refactor-backlog-setup/` (the arc before it).
Move both under `docs/project/archive/` with `git mv`, so history stays
intact through `git log --follow`. `SHOWCASE-ROADMAP.md` moves with them,
because it points at the now-archived backlog README.

Add `docs/project/archive/README.md`. It states that these trees are
historical and names the two directories that are still live.

Repoint every inbound reference named in RD-30's Files table: CLAUDE.md,
the root README, both backend READMEs, `LetterHtml.cs`, `a11y.mdx`, the
`document-feature` and `new-ssp` skills, and the readable-codebase PLAN,
README, and RD-19 ticket. Fix two upward-relative links inside the moved
WP files (WP-68, WP-69) that gained a directory level and would otherwise
break. Repoint `.prettierignore`'s two agent-prompt exclusions to their
new path, so prettier keeps leaving those files' exact wording alone.

Mark RD-30 done and check off its acceptance criteria; flip its README
row to done.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 23:00:38 +02:00

2.5 KiB

WP-49 — OpenZaak zaken read seam (slice 1)

Status: done Phase: 9 — OpenZaak / ZGW integration

Why

The POC serves cases from local SQLite. To grow toward production it must be able to source them from a real ZGW backend (OpenZaak) without changing the frontend (BFF-lite, ADR-0001). The backend had no data-access abstraction to swap behind, no outbound HTTP, and no JWT. This is the first thin vertical slice: read-only zaken.

Read first

Decisions (pre-made, don't relitigate)

  • OpenZaak's anti-corruption layer lives in the BFF, never the browser.
  • Source selected by config (Zgw:Enabled, default false) → POC still runs offline.
  • Each source maps into the existing ApplicationSummaryDto → no api-client drift, no FE change.
  • Fresh HS256 JWT per call (no refresh flow). Hand-rolled (no new NuGet).
  • IZaakSource is sync (matches the endpoint + local store); OpenZaak source does sync-over-async.

Files

  • Data/IZaakSource.cs, Data/LocalZaakSource.cs
  • Zgw/{ZgwOptions,ZgwTokenProvider,ZgwZaakMapper,OpenZaakZaakSource}.cs
  • Program.cs (DI + resolve IZaakSource in /admin/cases), appsettings.json (Zgw section)
  • tests: ZgwTokenProviderTests, ZgwZaakMapperTests, OpenZaakZaakSourceTests

Steps

  1. Extract the cases read into IZaakSource; LocalZaakSource delegates to ApplicationStore.
  2. Build the Zgw/ client (options, JWT minter, ZGW→DTO mapper, paginating HTTP source).
  3. Wire DI by config; refactor /admin/cases to resolve IZaakSource.
  4. Unit-test the minter, mapper, and source (fixtures + stub HttpMessageHandler).

Acceptance criteria

  • /admin/cases serves identical DTOs via LocalZaakSource (default, offline).
  • OpenZaakZaakSource follows pagination, resolves+caches zaaktype labels, sends a Bearer token.
  • JWT verifies (HS256) with the required claims.
  • dotnet test green (142); npm run ci green with no api-client drift (FE untouched).

Verification

cd backend && dotnet test; npm run ci; manual: /beheer/zaken still lists cases with Zgw:Enabled=false.

Out of scope

Create-zaak (WP-50), Documenten/DRC (WP-51), Notificaties (WP-52), real inbound OIDC/JWT, OpenZaak in docker-compose.

Risks

Sync-over-async blocks a thread under load if OpenZaak becomes the default → make the read path async then (noted at the call site).