Modern distributed systems rely heavily on event-driven architectures. These systems generate ordered streams of events that represent changes in the system. While live processing handles real-time workloads, many enterprise scenarios require running past events again. This process is called event replay.

An Event Replay System enables platforms to reprocess historical events without disrupting live users or corrupting state. This capability is critical when fixing corrupted projections, migrating systems, rebuilding read-models, or onboarding new services that must reprocess historical streams.

This article explains how to design a robust Event Replay System with a .NET backend and Angular frontend for orchestration.

Why Event Replay Matters

Event replay allows systems to:

It essentially treats history as a first-class component of system reliability.

Key Requirements

A production-ready replay system must:

Architectural Overview

A typical replay architecture includes:

Workflow Diagram

Admin Triggers Replay
       |
       v
Replay Manager API
       |
       v
Load Events from Event Store
       |
       v
Send to Replay Processor
       |
       v
Apply Rules (idempotent transformation, version upgrade if needed)
       |
       v
Write to Projections / Consumer Services
       |
       v
Track Status & Metrics

Flowchart

START
  |
  v
Select event stream or filter
  |
  v
Replay already running?
  |--YES--> Reject request
  |
  NO
  |
  v
Load next event in chronological order
  |
  v
Apply event handler
  |
  v
Success?
  |--NO--> Retry or mark failure and continue
  |
  YES
  |
  v
Progress > 100%?
  |--YES--> Mark replay complete
  |
  NO
  |
  v
Continue stream processing

Event Storage Model (SQL Example)

CREATE TABLE EventStream (
    Id BIGINT IDENTITY PRIMARY KEY,
    StreamId NVARCHAR(200),
    EventType NVARCHAR(200),
    EventBody NVARCHAR(MAX),
    Version INT,
    CreatedDate DATETIME2,
    IsProcessed BIT DEFAULT 0
);

Replay Metadata Table

CREATE TABLE ReplaySessions (
    ReplayId UNIQUEIDENTIFIER PRIMARY KEY,
    StreamId NVARCHAR(200),
    Status NVARCHAR(50),
    Progress DECIMAL(5,2),
    StartedAt DATETIME2,
    CompletedAt DATETIME2 NULL,
    FilterJson NVARCHAR(MAX)
);

Replay Service in .NET

public async Task ReplayAsync(Guid replayId)
{
    var session = await _registry.GetSession(replayId);

    var events = await _eventStore.LoadEvents(
        session.StreamId, 
        session.Filter);

    foreach (var evt in events)
    {
        try
        {
            await _handler.Process(evt, isReplay: true);
        }
        catch(Exception ex)
        {
            await _logger.LogReplayFailure(replayId, evt, ex);
        }

        await _registry.UpdateProgress(replayId);
    }

    await _registry.Complete(replayId);
}

Angular Admin UI Features

The UI allows:

Example Angular service call

startReplay(stream: string, filters: any): Observable<any> {
  return this.http.post(`/api/replay/start`, { stream, filters });
}

Ensuring Idempotency

Idempotency prevents double-processing. You can achieve this using:

Example

if(_projectionState.LastProcessedEventId >= event.Id)
    return;

Performance Strategies

Testing Strategy

Test TypePurpose
Dry-run replayValidate results without writing
Partial replayTest new feature or migration
Stress replayVerify performance under load
Versioned replayValidate schema migration or projection upgrade

Failure Handling

Real-World Best Practices

Conclusion

An Event Replay System transforms an event store from a passive log into an active resilience layer. It enables recovery, reprocessing, system migration, and analytical enrichment without disrupting live workloads. With proper isolation, idempotency safeguards, and monitoring, replay becomes a controlled and essential tool in enterprise-grade event-driven architectures.