feat(domain): implement the beoordeling use-case (goedkeuren/afwijzen) (refs #13)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-14 17:20:51 +02:00
parent 63699c3f55
commit d700659957

View File

@@ -0,0 +1,57 @@
using Big.Domain;
namespace Big.Application;
/// <summary>A behandelaar's beoordeling outcome, in domain language.</summary>
public enum BeoordelingsBesluit
{
/// <summary>Approve — enter the registration in the register.</summary>
Goedkeuren,
/// <summary>Reject — turn the registration down.</summary>
Afwijzen,
}
/// <summary>A behandelaar's decision on a registration.</summary>
public sealed record BeoordeelRegistratieCommand(RegistrationId RegistrationId, BeoordelingsBesluit Besluit);
/// <summary>
/// The beoordeling use case (S-12): apply a behandelaar's decision to a registration.
/// <see cref="BeoordelingsBesluit.Goedkeuren"/> sets the zaak's final status via the ACL (§8.1) and
/// advances the aggregate to INGESCHREVEN; <see cref="BeoordelingsBesluit.Afwijzen"/> 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.
/// </summary>
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);
}
}