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); }