namespace EventSubscriber.Application;
///
/// Projects inbound NRC notifications into the read projection. Tolerates duplicate and
/// out-of-order deliveries (CLAUDE.md §8.6): the notification log dedups, and the projection
/// upsert is idempotent on the register id. Rebuilds the projection by replaying the log.
///
public sealed class NotificationProjector(INotificationLog log, IProjectionStore store, IAclClient acl)
{
/// Handle one inbound notification. Reacts to a register record being written to
/// Objecten (S-19b-2, ADR-0030) and ignores everything else.
public async Task HandleAsync(Notification notification, CancellationToken ct = default)
{
ArgumentNullException.ThrowIfNull(notification);
if (!notification.IsRegisterRecordWritten)
return;
// S-19b-2: reading the record back through the ACL and projecting it lands with the
// implementation; today nothing reaches the store.
await Task.CompletedTask;
}
/// Rebuild the projection from the durable notification log (PRD §8.4).
public async Task RebuildAsync(CancellationToken ct = default)
{
await store.ClearAsync(ct);
foreach (var recorded in await log.AllAsync(ct))
await store.UpsertAsync(ToEntry(recorded), ct);
}
/// The projection row for an accepted notification. The log already holds exactly the
/// row's fields, so a rebuild needs no mapping rules and no upstream reads. bsn/naam stay
/// deferred — the register record is public-safe by construction (ADR-0027).
private static RegisterEntry ToEntry(RecordedNotification recorded)
=> new(recorded.RegisterId, recorded.Status, recorded.Reference);
}