using Big.Domain; namespace Big.Application; /// A behandelaar's beoordeling outcome, in domain language. public enum BeoordelingsBesluit { /// Approve — enter the registration in the register. Goedkeuren, /// Reject — turn the registration down. Afwijzen, } /// A behandelaar's decision on a registration. public sealed record BeoordeelRegistratieCommand(RegistrationId RegistrationId, BeoordelingsBesluit Besluit); /// /// The beoordeling use case (S-12): apply a behandelaar's decision to a registration. /// sets the zaak's final status via the ACL (§8.1) and /// advances the aggregate to INGESCHREVEN; advances it to /// AFGEWEZEN in the domain (propagating a rejection to the zaak, so the openbaar projection reflects /// it, is a later sub-slice of S-12). After applying the decision it completes the Flowable /// Beoordelen task (found by registrationId) so the workflow advances (ADR-0013). Both /// decisions are idempotent — a repeated or redelivered decision that matches the current terminal /// state is a no-op, so the ACL is not called and the task not completed twice. /// public sealed class BeoordeelRegistratie(IRegistrationStore store, IAclClient acl, IUserTaskClient tasks) { public async Task HandleAsync(BeoordeelRegistratieCommand command, CancellationToken ct = default) { ArgumentNullException.ThrowIfNull(command); var registration = await store.GetAsync(command.RegistrationId, ct) ?? throw new InvalidOperationException($"No registration {command.RegistrationId} to decide."); switch (command.Besluit) { case BeoordelingsBesluit.Goedkeuren: // A repeated approval is a no-op: don't set the zaak status a second time. if (registration.Status == RegistrationStatus.Ingeschreven) return; if (registration.ZaakUrl is null) throw new InvalidOperationException( $"Registration {command.RegistrationId} has no zaak yet; it cannot be approved."); await acl.ApproveZaakAsync(registration.ZaakUrl, ct); registration.Approve(); break; case BeoordelingsBesluit.Afwijzen: if (registration.Status == RegistrationStatus.Afgewezen) return; registration.Reject(); break; } await store.SaveAsync(registration, ct); await CompleteWorkflowTaskAsync(command.RegistrationId, command.Besluit, ct); } // Advance the workflow: complete the open Beoordelen task for this registration. If none is open // (already completed, or the process hasn't parked yet) the decision still stands — we complete // nothing rather than fail. private async Task CompleteWorkflowTaskAsync(RegistrationId registrationId, BeoordelingsBesluit besluit, CancellationToken ct) { var open = await tasks.GetOpenBeoordelingenAsync(ct); var task = open.FirstOrDefault(t => t.RegistrationId == registrationId); if (task is not null) await tasks.CompleteBeoordelingAsync(task.TaskId, besluit, ct); } }