feat(domain): withdrawal cancels the registratie process via a BPMN message event (refs #12)
Add an interrupting message boundary event (RegistratieIngetrokken) on the Beoordelen task that ends the process; the Workflow Client delivers the message to the task's execution, and the withdraw handler triggers it best-effort after the domain transition (ADR-0014). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,13 +7,14 @@ public sealed record WithdrawRegistrationCommand(RegistrationId RegistrationId);
|
||||
|
||||
/// <summary>
|
||||
/// The withdrawal use case (S-11): a zorgprofessional pulls a still-open registration back. It
|
||||
/// advances the aggregate to INGETROKKEN and persists it. Idempotent — a repeated or redelivered
|
||||
/// withdrawal of an already-withdrawn registration is a no-op (the aggregate is not persisted again).
|
||||
/// Cancelling the running Flowable process (so the case leaves the behandelaar's werkbak) is a later
|
||||
/// sub-slice (S-11b, via a BPMN message event); this sub-slice owns the domain transition only —
|
||||
/// mirroring how the beoordeling's rejection deferred its zaak propagation.
|
||||
/// advances the aggregate to INGETROKKEN, persists it, then cancels the running registratie process
|
||||
/// by delivering the withdrawal message to its open <c>Beoordelen</c> task (ADR-0014), so the case
|
||||
/// leaves the behandelaar's werkbak. Idempotent — a repeated or redelivered withdrawal of an
|
||||
/// already-withdrawn registration is a no-op (not persisted or cancelled again). Cancelling is
|
||||
/// best-effort: if no <c>Beoordelen</c> task is open (the process has not parked there yet, or has
|
||||
/// already ended) the withdrawal still stands — mirroring <see cref="BeoordeelRegistratie"/>.
|
||||
/// </summary>
|
||||
public sealed class WithdrawRegistration(IRegistrationStore store)
|
||||
public sealed class WithdrawRegistration(IRegistrationStore store, IUserTaskClient tasks)
|
||||
{
|
||||
public async Task HandleAsync(WithdrawRegistrationCommand command, CancellationToken ct = default)
|
||||
{
|
||||
@@ -22,11 +23,22 @@ public sealed class WithdrawRegistration(IRegistrationStore store)
|
||||
var registration = await store.GetAsync(command.RegistrationId, ct)
|
||||
?? throw new InvalidOperationException($"No registration {command.RegistrationId} to withdraw.");
|
||||
|
||||
// A repeated withdrawal is a no-op: don't persist the already-withdrawn aggregate again.
|
||||
// A repeated withdrawal is a no-op: don't persist or cancel the already-withdrawn one again.
|
||||
if (registration.Status == RegistrationStatus.Ingetrokken)
|
||||
return;
|
||||
|
||||
registration.Withdraw();
|
||||
await store.SaveAsync(registration, ct);
|
||||
await CancelWorkflowTaskAsync(command.RegistrationId, ct);
|
||||
}
|
||||
|
||||
// Cancel the workflow: deliver the withdrawal message to the open Beoordelen task's execution so
|
||||
// the process's boundary event ends it. If none is open the withdrawal still stands.
|
||||
private async Task CancelWorkflowTaskAsync(RegistrationId registrationId, CancellationToken ct)
|
||||
{
|
||||
var open = await tasks.GetOpenBeoordelingenAsync(ct);
|
||||
var task = open.FirstOrDefault(t => t.RegistrationId == registrationId);
|
||||
if (task is not null)
|
||||
await tasks.WithdrawBeoordelingAsync(task.ExecutionId, ct);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user