feat(domain): RegistratieVerlopen worker + processor expire on document timeout (refs #102)

Adds RegistratieVerlopenJob, IRegistratieVerlopenClient, the ExpireRegistrationWorker
application handler, and the RegistratieVerlopenProcessor drain loop — the timeout
counterpart to the OpenZaak/escalation worker trios.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
not
2026-07-20 09:44:15 +02:00
co-authored by Claude Opus 4.8
parent 5add817c10
commit c536c965de
4 changed files with 99 additions and 0 deletions
@@ -0,0 +1,34 @@
using Big.Domain;
namespace Big.Application;
/// <summary>
/// Handles one acquired <c>RegistratieVerlopen</c> external-worker job (S-10a, ADR-0017): load the
/// registration the job correlates to and expire it to VERLOPEN — the 30-day document-wait timer fired
/// before the documents arrived, so the case is cancelled. Pure application logic over ports; it knows
/// nothing of Flowable. The polling loop that feeds it jobs lives in Infrastructure. Mirrors
/// <see cref="OpenZaakWorker"/>.
/// </summary>
public sealed class ExpireRegistrationWorker(IRegistrationStore store)
{
/// <summary>
/// Process the job. Idempotent: a redelivered job whose registration is already VERLOPEN is a
/// no-op — not persisted again (§8.6, at-least-once delivery). An unknown registration is an error:
/// it throws, leaving the job un-completed for Flowable to redeliver.
/// </summary>
public async Task HandleAsync(RegistratieVerlopenJob job, CancellationToken ct = default)
{
ArgumentNullException.ThrowIfNull(job);
var registration = await store.GetAsync(job.RegistrationId, ct)
?? throw new InvalidOperationException(
$"No registration {job.RegistrationId} for RegistratieVerlopen job {job.JobId}.");
// A redelivered job whose registration is already VERLOPEN completes without persisting again.
if (registration.Status == RegistrationStatus.Verlopen)
return;
registration.Expire();
await store.SaveAsync(registration, ct);
}
}
+8
View File
@@ -95,3 +95,11 @@ public sealed record OpenZaakJob(string JobId, RegistrationId RegistrationId);
/// once the 14-day boundary timer fires (ADR-0015).
/// </summary>
public sealed record EscalatieJob(string JobId, string ProcessInstanceId);
/// <summary>
/// An acquired <c>RegistratieVerlopen</c> job (S-10a): the Flowable job id and the registration id it
/// carries as a process variable. The 30-day boundary timer on <c>WachtOpDocumenten</c> spawns it when
/// the required documents were not supplied in time; expiring the correlated registration to VERLOPEN
/// cancels the case (ADR-0017).
/// </summary>
public sealed record RegistratieVerlopenJob(string JobId, RegistrationId RegistrationId);