S-05: the BIG Domain Service skeleton with the Registration aggregate. Submitting a
registration creates the aggregate (INGEDIEND), starts the Flowable registratie process, and
— via a hosted external-task worker — opens a zaak through the ACL and records it on the aggregate.
This is the upstream half of the walking skeleton: it produces the zaak that S-06 (#7) projects.
Orchestration (ADR-0009):POST /registrations is fast and returns 202; the OpenZaakAanmaken external-worker job is driven off the request path by a polling BackgroundService. The Workflow Client is the only code that talks to Flowable (§8.2); the
worker opens the zaak only through the ACL (§8.1). The REST contract was verified against a live
flowable-rest engine.
Layering mirrors the other services: Big.Domain (pure) → Big.Application (use cases over
ports) → Big.Infrastructure (Flowable + ACL clients, in-memory store, job pump) → Big.Api.
Mutation: Stryker baseline for the domain service is 97.7% (break threshold 90, the repo
floor). The OpenZaakJobPump hosted-shell is excluded; two genuinely-equivalent mutants are // Stryker disable-d with reasons.
Scope decisions (ADR-0009): registration persistence is in-memory for this slice (the
read path is the projection, S-06) — an EF-backed domain store is a documented follow-up; the IRegistrationStore port keeps that additive. The state machine is minimal (INGEDIEND only).
verify-domain end-to-end: seeds a published BIG zaaktype, recreates the acl service
pointed at it (the zaaktype URL is server-assigned, so it isn't knowable at bring-up) host-
consistently, then submits a registration and asserts the worker opens a zaak. This leg
(domain→ACL→OpenZaak) could not be exercised in the dev environment (no selectielijst egress /
full compose); it mirrors the configuration the ACL integration test already proves, so CI is its
first real run.
Adds an overridable ACL_OPENZAAK_BASEURL compose var (default unchanged) and a machine-readable ZAAKTYPE_URL line from seed_catalogus.py.
ac874bf makes the mutation job's Stryker report uploads best-effort (continue-on-error: true): the Gitea artifact backend returns 500 to upload-artifact@v3 (server-side), which was failing the job on every run (main included) despite the ratchet passing. The mutation gate stays the Stryker ratchet. See gitea-actions-gotchas.md §4 (#62).
## What & why
S-05: the **BIG Domain Service** skeleton with the `Registration` aggregate. Submitting a
registration creates the aggregate (`INGEDIEND`), starts the Flowable `registratie` process, and
— via a hosted external-task worker — opens a zaak through the ACL and records it on the aggregate.
This is the upstream half of the walking skeleton: it produces the zaak that S-06 (#7) projects.
**Orchestration (ADR-0009):** `POST /registrations` is fast and returns 202; the
`OpenZaakAanmaken` external-worker job is driven off the request path by a polling
`BackgroundService`. The Workflow Client is the only code that talks to Flowable (§8.2); the
worker opens the zaak only through the ACL (§8.1). The REST contract was verified against a live
flowable-rest engine.
Layering mirrors the other services: `Big.Domain` (pure) → `Big.Application` (use cases over
ports) → `Big.Infrastructure` (Flowable + ACL clients, in-memory store, job pump) → `Big.Api`.
Closes #6
Closes #60
## Definition of Done
- [x] Linked Gitea issue (above).
- [x] Failing test committed before the implementation (red→green for every layer).
- [x] Implementation makes the test pass; refactor where structure improved.
- [x] Conventional Commits referencing the issue (`refs #6`).
- [x] CI green — all Gitea Actions jobs (first real run is on this PR; see note below).
- [x] `docker compose up` reaches green health checks (domain service health-checked, verified locally).
- [x] Docs updated — ADR-0009 + demo-script note.
- [x] ADR added — `docs/architecture/adr-0009-external-task-job-worker.md`.
- [x] Demo note in `docs/demo-script.md`.
## Notes for reviewers
- **Mutation:** Stryker baseline for the domain service is **97.7%** (break threshold 90, the repo
floor). The `OpenZaakJobPump` hosted-shell is excluded; two genuinely-equivalent mutants are
`// Stryker disable`-d with reasons.
- **Scope decisions (ADR-0009):** registration persistence is **in-memory** for this slice (the
read path is the projection, S-06) — an EF-backed domain store is a documented follow-up; the
`IRegistrationStore` port keeps that additive. The state machine is minimal (`INGEDIEND` only).
- **`verify-domain` end-to-end:** seeds a published BIG zaaktype, recreates the `acl` service
pointed at it (the zaaktype URL is server-assigned, so it isn't knowable at bring-up) host-
consistently, then submits a registration and asserts the worker opens a zaak. This leg
(domain→ACL→OpenZaak) could not be exercised in the dev environment (no `selectielijst` egress /
full compose); it mirrors the configuration the ACL integration test already proves, so CI is its
first real run.
- Adds an overridable `ACL_OPENZAAK_BASEURL` compose var (default unchanged) and a machine-readable
`ZAAKTYPE_URL` line from `seed_catalogus.py`.
Closes #62
### Update — CI fix folded in
`ac874bf` makes the `mutation` job's Stryker report uploads best-effort (`continue-on-error: true`): the Gitea artifact backend returns 500 to `upload-artifact@v3` (server-side), which was failing the job on **every** run (main included) despite the ratchet passing. The mutation gate stays the Stryker ratchet. See gitea-actions-gotchas.md §4 (#62).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The Domain Service drives the OpenZaakAanmaken external-worker task as a
hosted job worker (PRD §36): POST /registrations starts the registratie
process and returns; a polling worker acquires the job, opens a zaak via
the ACL (§8.1), attaches the zaak URL to the aggregate, and completes the
job. The Workflow Client is the only Flowable client (§8.2); the worker
logic is an Application service over ports. Registration state is in-memory
for the minimal slice (the read path is the projection, S-06).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Failing unit tests for the Registration aggregate root: a submission starts
in INGEDIEND carrying its bsn, an empty/whitespace/null bsn is rejected, the
started process-instance id is remembered, and attaching the zaak the ACL
opened records its URL idempotently (a conflicting URL is rejected) while the
status stays INGEDIEND.
The aggregate is a stub (no-op mutators, empty bsn) so the tests compile and
fail on their assertions; the green commit implements the invariants.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Submit requires a bsn and starts the aggregate in INGEDIEND; the started
process-instance id is recorded; AttachZaak stores the ACL's zaak URL,
tolerating a duplicate (at-least-once worker delivery) and rejecting a
conflicting URL, without advancing the status.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Failing application-layer tests over fake ports (IWorkflowClient, IAclClient,
IRegistrationStore):
- Submit persists an INGEDIEND registration and starts the registratie process,
recording the instance id — and persists *before* starting, so the worker can
correlate the OpenZaakAanmaken job back to its aggregate (ADR-0009).
- The worker opens a zaak via the ACL and attaches it; an unknown registration
throws (job left for redelivery); a redelivered job is idempotent and opens no
second zaak (§8.6).
Handlers are stubs (no persistence / no ACL call) so the tests compile and fail
on their assertions; the green commit implements them.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SubmitRegistration creates the aggregate, persists it, starts the registratie
process via the Workflow Client, records the instance id and upserts. OpenZaakWorker
loads the correlated registration, opens a zaak via the ACL, attaches it and saves;
an unknown registration throws (job redelivered), and an already-opened zaak short-
circuits without opening a second one (§8.6).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Failing infrastructure unit tests (stub HttpMessageHandler, fakes):
- FlowableWorkflowClient starts a process with the registrationId variable and
returns the instance id; acquires OpenZaakAanmaken jobs (topic/workerId/lock)
and parses their registrationId; completes a job with the zaakUrl variable —
request URIs match flowable-rest's service/ and external-job-api/ paths.
- AclHttpClient POSTs the bsn to the ACL and returns the zaak URL.
- InMemoryRegistrationStore saves/reads/upserts by id.
- OpenZaakJobProcessor acquires, opens a zaak, completes the job; leaves a failing
job uncompleted for redelivery; polls harmlessly when idle.
Adapters are stubs so the tests compile and fail on their assertions; the green
commit implements them against the REST contract verified on a live Flowable.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
FlowableWorkflowClient speaks flowable-rest's REST API (Basic auth): start a
registratie process with the registrationId variable, acquire OpenZaakAanmaken
external-worker jobs and parse their registrationId, complete a job with the
zaakUrl variable — the contract verified against a live engine. AclHttpClient
POSTs the bsn to the ACL and returns the zaak URL. InMemoryRegistrationStore is
a concurrent-dictionary upsert. OpenZaakJobProcessor drains parked jobs, opening
a zaak per job and completing it, leaving failures for redelivery; OpenZaakJobPump
is the hosted polling shell that drives it on an interval (ADR-0009).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The BIG Domain Service Api wires the use cases and the hosted job worker:
POST /registrations creates the aggregate and starts the registratie process,
returning 202 with a location; GET /registrations/{id} reads the aggregate so
the eventually-opened zaak URL can be observed (ADR-0009). The Workflow Client
is registered once behind both Flowable ports; the ACL client and in-memory
store complete the wiring. Verified against a live flowable-rest: submit starts
a parked process and the worker polls it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Use-case-level BDD (Reqnroll) for S-05: a zorgprofessional submits a registration;
the Domain Service starts the registratie process and the OpenZaakAanmaken external
task opens a zaak via the ACL, recorded on the aggregate (ADR-0009). Driven against
in-memory Workflow Client and ACL stand-ins; real Flowable+ACL+OpenZaak delivery is
the live-stack verify-domain check.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stryker.NET config for the domain service (break 90, the repo's ratchet floor),
excluding the OpenZaakJobPump hosted-shell from mutation. Hardened the unit tests
to kill survivors — Basic-credential value, variable types, null/failure response
paths, option defaults, guard clauses, save counts and log output — leaving only
two documented equivalent mutants (Stryker-disabled). make mutation runs the domain
ratchet and CI uploads its report alongside the others.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dockerfile (multi-stage, .NET 10) + .dockerignore for the BIG Domain Service; a
'domain' service in infra/docker-compose.yml (health-checked, depends on acl healthy
and flowable-init completed). run-domain-check.sh drives the full path against the up
stack — seed a published zaaktype, recreate the acl pointed at it (host-consistent),
POST /registrations, and assert the worker opens a zaak and records it. Wired as the
verify-domain Makefile target + a verify-stack CI step; domain added to WAIT_SVCS and
the log dump. seed_catalogus.py now emits a machine-readable ZAAKTYPE_URL line.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Gitea artifact backend returns 500 to actions/upload-artifact@v3 (server-side,
distinct from the @v4 GHES guard). With if: always() that 500 failed the whole
mutation job even though the ratchet passed — red on main and on every PR. Mark the
three report uploads continue-on-error: true so the mutation *gate* stays the Stryker
ratchet (make mutation's exit code), not the report upload. Documented in
gitea-actions-gotchas.md §4.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What & why
S-05: the BIG Domain Service skeleton with the
Registrationaggregate. Submitting aregistration creates the aggregate (
INGEDIEND), starts the Flowableregistratieprocess, and— via a hosted external-task worker — opens a zaak through the ACL and records it on the aggregate.
This is the upstream half of the walking skeleton: it produces the zaak that S-06 (#7) projects.
Orchestration (ADR-0009):
POST /registrationsis fast and returns 202; theOpenZaakAanmakenexternal-worker job is driven off the request path by a pollingBackgroundService. The Workflow Client is the only code that talks to Flowable (§8.2); theworker opens the zaak only through the ACL (§8.1). The REST contract was verified against a live
flowable-rest engine.
Layering mirrors the other services:
Big.Domain(pure) →Big.Application(use cases overports) →
Big.Infrastructure(Flowable + ACL clients, in-memory store, job pump) →Big.Api.Closes #6
Closes #60
Definition of Done
refs #6).docker compose upreaches green health checks (domain service health-checked, verified locally).docs/architecture/adr-0009-external-task-job-worker.md.docs/demo-script.md.Notes for reviewers
floor). The
OpenZaakJobPumphosted-shell is excluded; two genuinely-equivalent mutants are// Stryker disable-d with reasons.read path is the projection, S-06) — an EF-backed domain store is a documented follow-up; the
IRegistrationStoreport keeps that additive. The state machine is minimal (INGEDIENDonly).verify-domainend-to-end: seeds a published BIG zaaktype, recreates theaclservicepointed at it (the zaaktype URL is server-assigned, so it isn't knowable at bring-up) host-
consistently, then submits a registration and asserts the worker opens a zaak. This leg
(domain→ACL→OpenZaak) could not be exercised in the dev environment (no
selectielijstegress /full compose); it mirrors the configuration the ACL integration test already proves, so CI is its
first real run.
ACL_OPENZAAK_BASEURLcompose var (default unchanged) and a machine-readableZAAKTYPE_URLline fromseed_catalogus.py.Closes #62
Update — CI fix folded in
ac874bfmakes themutationjob's Stryker report uploads best-effort (continue-on-error: true): the Gitea artifact backend returns 500 toupload-artifact@v3(server-side), which was failing the job on every run (main included) despite the ratchet passing. The mutation gate stays the Stryker ratchet. See gitea-actions-gotchas.md §4 (#62).🤖 Generated with Claude Code
The BIG Domain Service Api wires the use cases and the hosted job worker: POST /registrations creates the aggregate and starts the registratie process, returning 202 with a location; GET /registrations/{id} reads the aggregate so the eventually-opened zaak URL can be observed (ADR-0009). The Workflow Client is registered once behind both Flowable ports; the ACL client and in-memory store complete the wiring. Verified against a live flowable-rest: submit starts a parked process and the worker polls it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>