Files
register-referentie/services/event-subscriber/EventSubscriber.Application/Ports.cs
Niek Otten 566fc9bcea feat(event-subscriber): enrich the projection with the zaak reference via the ACL (refs #78)
On each notification the subscriber reads the zaak's reference (identificatie)
through the ACL — the only code allowed to talk to ZGW (§8.1) — and persists it on
the register_projection row and in the processed_notifications replay log. Storing
it in the log keeps rebuild log-only (ADR-0008): no ACL/ZGW access on rebuild.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 14:46:48 +02:00

51 lines
2.6 KiB
C#

namespace EventSubscriber.Application;
/// <summary>
/// The durable log of notifications the subscriber has accepted. It is both the idempotency
/// guard (a replayed notification is recognised and dropped) and the rebuild source: the
/// projection is a derived artefact (PRD §8.4) regenerated by replaying this log, so a rebuild
/// needs no access to OpenZaak (CLAUDE.md §8.1). Implemented in Infrastructure over Postgres.
/// </summary>
public interface INotificationLog
{
/// <summary>
/// Record a notification. Returns <c>true</c> if it was newly recorded, <c>false</c> if an
/// entry with the same key already existed (a duplicate delivery). Must be atomic so that
/// concurrent duplicate deliveries cannot both observe <c>true</c>.
/// </summary>
Task<bool> TryRecordAsync(RecordedNotification notification, CancellationToken ct = default);
/// <summary>Every accepted notification, for rebuilding the projection.</summary>
Task<IReadOnlyList<RecordedNotification>> AllAsync(CancellationToken ct = default);
}
/// <summary>A notification that has been accepted, retaining what a rebuild needs to recompute its
/// projection row — the ZGW <c>resource</c> (zaak-create → INGEDIEND vs status-set → INGESCHREVEN) and
/// the zaak <c>reference</c> (identificatie), so a rebuild reproduces the row without re-reading ZGW (#78).</summary>
public sealed record RecordedNotification(string Key, string Actie, string ZaakId, string Resource, string? Reference);
/// <summary>
/// Port to the Anti-Corruption Layer. The subscriber enriches the projection with the zaak's
/// public-safe reference (its identificatie) by asking the ACL — the only code that may read ZGW
/// (§8.1) — rather than reading OpenZaak itself (adr-proposal #78).
/// </summary>
public interface IAclClient
{
/// <summary>The zaak's reference (identificatie) for the read projection.</summary>
Task<string> GetZaakReferenceAsync(Uri zaakUrl, CancellationToken ct = default);
}
/// <summary>The read projection store. Owned by the projection bounded context (ADR-0008); the
/// subscriber writes to it and the projection-api reads it.</summary>
public interface IProjectionStore
{
/// <summary>Insert or update the row for <see cref="RegisterEntry.Id"/>. Idempotent on the id.</summary>
Task UpsertAsync(RegisterEntry entry, CancellationToken ct = default);
/// <summary>Remove every row — the first step of a rebuild.</summary>
Task ClearAsync(CancellationToken ct = default);
/// <summary>Every projection row (used by tests and the rebuild verification).</summary>
Task<IReadOnlyList<RegisterEntry>> AllAsync(CancellationToken ct = default);
}