From d700659957644ad597d49104165d3c6c6ecda534 Mon Sep 17 00:00:00 2001 From: Niek Otten Date: Tue, 14 Jul 2026 17:20:51 +0200 Subject: [PATCH] feat(domain): implement the beoordeling use-case (goedkeuren/afwijzen) (refs #13) Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Big.Application/BeoordeelRegistratie.cs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 services/domain/Big.Application/BeoordeelRegistratie.cs diff --git a/services/domain/Big.Application/BeoordeelRegistratie.cs b/services/domain/Big.Application/BeoordeelRegistratie.cs new file mode 100644 index 0000000..f0a384f --- /dev/null +++ b/services/domain/Big.Application/BeoordeelRegistratie.cs @@ -0,0 +1,57 @@ +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). 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 twice. +/// +public sealed class BeoordeelRegistratie(IRegistrationStore store, IAclClient acl) +{ + 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); + } +}