Files
register-referentie/docs/architecture/adr-0015-beoordeling-escalation.md
Niek Otten cb8568c36a docs(workflow): ADR-0015 + demo note for beoordeling escalation (S-14, refs #15)
Record the escalation-via-external-worker decision (ADR-0015, from proposal #98)
and add the S-14 demo walkthrough that fires the 14-day timer early via Flowable's
management API to observe the reassignment to teamlead.

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

4.7 KiB

ADR-0015: Beoordeling escalation reassigns via an external-worker task

  • Status: Accepted
  • Date: 2026-07-17
  • Deciders: Respellion engineering
  • Relates to: S-14 (#15); proposal #98. Builds on ADR-0009 (external-task worker / Workflow Client), ADR-0013 (behandel-portal wiring, the Beoordelen user task), ADR-0014 (the boundary-event pattern on Beoordelen).

Context

S-14 escalates a beoordeling that a behandelaar does not pick up in time: after 14 days the case must move to the teamlead role (PRD §5, flow 5). The Beoordelen user task already exists, claimable by the behandelaar candidate group; the teamlead role is seeded in the medewerker realm.

Two forces shape this.

  1. The task must stay open. Escalation changes who may claim an unclaimed beoordeling, not the work itself — so the timer must be non-interrupting: the Beoordelen task keeps running while escalation happens alongside it.
  2. Reassigning an open task's candidate group needs code. Flowable cannot rewrite the candidate groups of an already-open user task from BPMN XML alone — that requires either a Java delegate/listener embedded in the engine, or an out-of-process actor driving the REST API. The repository has held a "stock Flowable image, no custom jars; the Workflow Client is the only code that talks to Flowable (§8.2)" posture since ADR-0009.

Decision

A non-interrupting P14D boundary timer on Beoordelen fires an external-worker task (BeoordelingEscaleren); the Workflow Client reassigns the still-open Beoordelen task from the behandelaar group to teamlead.

  • Modelled in BPMN, driven by an external worker. The timer routes a parallel token to an external-worker service task on the BeoordelingEscaleren topic, ending at a dedicated "Beoordeling geëscaleerd" end event. The model owns when escalation happens; the Workflow Client — the only code that talks to Flowable (§8.2) — owns how the reassignment is applied, exactly as OpenZaakAanmaken delegates the ZGW call (ADR-0009). No custom code runs inside Flowable.
  • Reassignment is a candidate-group swap. The escalation worker finds the still-open Beoordelen task in the escalating instance (task query by processInstanceId + taskDefinitionKey), adds teamlead as a candidate group via the task identity links, then removes behandelaar. The task now belongs to the teamlead; its history and variables are untouched.
  • Best-effort, mirroring beoordeling and withdrawal. If the task is no longer open — the behandelaar completed it in the window before the timer fired — the reassignment is a no-op. A failed reassignment leaves the escalation job un-completed so Flowable redelivers it (§8.6), consistent with the OpenZaakAanmaken worker.
  • Segregated interface. The escalation methods live on IBeoordelingEscalatieClient, separate from the OpenZaakAanmaken worker's IExternalWorkerClient, so the OpenZaak worker never sees escalation (interface segregation). Both are implemented by the one FlowableWorkflowClient.

Consequences

Positive

  • The escalation trigger is visible in registratie.bpmn; Flowable stays a stock image, and the Workflow Client remains the sole Flowable client (§8.2 upheld, not bent).
  • Reuses the external-worker mechanics (topic acquire/complete, hosted pump, per-tick scope, redelivery-on-failure) wholesale — the new code is one client capability, one processor, one pump.
  • Escalation latency is bounded by the worker's poll interval (seconds) — negligible against a 14-day timer.

Negative / costs

  • Escalation is two REST hops (add teamlead, remove behandelaar) rather than one atomic update; between them the task is briefly claimable by both groups. Harmless at these volumes, and the pair is idempotent on redelivery.
  • The Flowable identity-link and management-job REST shapes are validated live (verify-domain fires the timer early via the management API), not in the Workflow Client's unit tests, which stub the HTTP exchange and assert only the request shape — consistent with ADR-0009 and ADR-0014.

Alternatives considered

  • Flowable timer/task listener (Java delegate). Reassign in-engine when the timer fires. Rejected: it needs a custom jar in Flowable, breaking the stock-image, REST-only posture and adding a build/deploy surface to the engine for no capability the external-worker route lacks.
  • Interrupting timer that re-creates the task for teamlead. Cancel Beoordelen and start a fresh teamlead task. Rejected: it loses the task's identity/history and complicates correlation, where a candidate-group swap on the same task expresses "the same work, now the teamlead's" directly.