test(domain): SubmitRegistration + OpenZaakWorker use cases (refs #6)
Failing application-layer tests over fake ports (IWorkflowClient, IAclClient, IRegistrationStore): - Submit persists an INGEDIEND registration and starts the registratie process, recording the instance id — and persists *before* starting, so the worker can correlate the OpenZaakAanmaken job back to its aggregate (ADR-0009). - The worker opens a zaak via the ACL and attaches it; an unknown registration throws (job left for redelivery); a redelivered job is idempotent and opens no second zaak (§8.6). Handlers are stubs (no persistence / no ACL call) so the tests compile and fail on their assertions; the green commit implements them. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
15
services/domain/Big.Application/Big.Application.csproj
Normal file
15
services/domain/Big.Application/Big.Application.csproj
Normal file
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<!-- The application layer (CLAUDE.md §9): use cases over ports. Depends on Domain only;
|
||||
Infrastructure implements the ports. -->
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Big.Domain\Big.Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
27
services/domain/Big.Application/OpenZaakWorker.cs
Normal file
27
services/domain/Big.Application/OpenZaakWorker.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Big.Domain;
|
||||
|
||||
namespace Big.Application;
|
||||
|
||||
/// <summary>
|
||||
/// Handles one acquired <c>OpenZaakAanmaken</c> external-worker job (ADR-0009): load the registration
|
||||
/// the job correlates to, open a zaak for it via the ACL (§8.1), attach the zaak to the aggregate, and
|
||||
/// return the zaak URL so the caller can complete the Flowable job. Pure application logic over ports —
|
||||
/// it knows nothing of Flowable; the polling loop that feeds it jobs lives in Infrastructure.
|
||||
/// </summary>
|
||||
public sealed class OpenZaakWorker(IRegistrationStore store, IAclClient acl)
|
||||
{
|
||||
/// <summary>
|
||||
/// Process the job and return the URL of the (existing or newly opened) zaak. Idempotent: if the
|
||||
/// registration already has a zaak — a job redelivered after its completion was lost — it returns
|
||||
/// that zaak without opening a second one (§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<Uri> HandleAsync(OpenZaakJob job, CancellationToken ct = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(job);
|
||||
|
||||
// STUB (red): does not load, call the ACL, or attach.
|
||||
await Task.CompletedTask;
|
||||
return new Uri("about:blank");
|
||||
}
|
||||
}
|
||||
47
services/domain/Big.Application/Ports.cs
Normal file
47
services/domain/Big.Application/Ports.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Big.Domain;
|
||||
|
||||
namespace Big.Application;
|
||||
|
||||
/// <summary>
|
||||
/// The port to the workflow engine. Implemented by the Workflow Client in Infrastructure — the
|
||||
/// <em>only</em> code that talks to Flowable (CLAUDE.md §8.2). The application asks it to start the
|
||||
/// registratie process; it never knows Flowable exists.
|
||||
/// </summary>
|
||||
public interface IWorkflowClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Start one <c>registratie</c> process instance for the given registration, carrying the
|
||||
/// registration id so the <c>OpenZaakAanmaken</c> external task can be correlated back to its
|
||||
/// aggregate. Returns the process instance id.
|
||||
/// </summary>
|
||||
Task<string> StartRegistrationProcessAsync(RegistrationId registrationId, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The port to the Anti-Corruption Layer. Implemented in Infrastructure by an HTTP client to the
|
||||
/// ACL service — the <em>only</em> code that talks to ZGW (CLAUDE.md §8.1). The domain hands over a
|
||||
/// bsn; the ACL default-fills the ZGW-mandatory fields (ADR-0003) and returns the created zaak URL.
|
||||
/// </summary>
|
||||
public interface IAclClient
|
||||
{
|
||||
Task<Uri> OpenZaakAsync(string bsn, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Persistence port for the <see cref="Registration"/> aggregate. In-memory for the minimal slice
|
||||
/// (ADR-0009); an EF-backed store is a documented follow-up, and this port keeps that change additive.
|
||||
/// </summary>
|
||||
public interface IRegistrationStore
|
||||
{
|
||||
/// <summary>Insert or update the registration, keyed on its id.</summary>
|
||||
Task SaveAsync(Registration registration, CancellationToken ct = default);
|
||||
|
||||
/// <summary>Load a registration by id, or <c>null</c> if none exists.</summary>
|
||||
Task<Registration?> GetAsync(RegistrationId id, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An acquired <c>OpenZaakAanmaken</c> external-worker job: the Flowable job id (needed to complete
|
||||
/// it) and the registration id it carries as a process variable.
|
||||
/// </summary>
|
||||
public sealed record OpenZaakJob(string JobId, RegistrationId RegistrationId);
|
||||
25
services/domain/Big.Application/SubmitRegistration.cs
Normal file
25
services/domain/Big.Application/SubmitRegistration.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Big.Domain;
|
||||
|
||||
namespace Big.Application;
|
||||
|
||||
/// <summary>A zorgprofessional's request to register, in domain language. No ZGW concepts.</summary>
|
||||
public sealed record SubmitRegistrationCommand(string Bsn);
|
||||
|
||||
/// <summary>
|
||||
/// The submit use case: create the <see cref="Registration"/> aggregate (INGEDIEND), persist it,
|
||||
/// then start the registratie workflow process and record its instance id. It returns as soon as
|
||||
/// the process is started — opening the zaak happens later, off the request path, in the external-task
|
||||
/// worker (ADR-0009). Persisting <em>before</em> starting the process closes the race where the worker
|
||||
/// acquires the OpenZaakAanmaken job before the aggregate it correlates to exists.
|
||||
/// </summary>
|
||||
public sealed class SubmitRegistration(IRegistrationStore store, IWorkflowClient workflow)
|
||||
{
|
||||
public async Task<RegistrationId> HandleAsync(SubmitRegistrationCommand command, CancellationToken ct = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(command);
|
||||
|
||||
// STUB (red): no persistence, no process start.
|
||||
await Task.CompletedTask;
|
||||
return RegistrationId.New();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user