dotnet-cqrs/Svrnty.CQRS.Events.Abstractions/Projections/IProjection.cs

94 lines
3.6 KiB
C#

using System.Threading;
using Svrnty.CQRS.Events.Abstractions.EventStore;
using System.Threading.Tasks;
namespace Svrnty.CQRS.Events.Abstractions.Projections;
/// <summary>
/// Represents a projection that processes events to build a read model.
/// </summary>
/// <typeparam name="TEvent">The type of event this projection handles.</typeparam>
/// <remarks>
/// <para>
/// <strong>Phase 7 Feature - Event Sourcing Projections:</strong>
/// Projections consume events from streams and build queryable read models.
/// Each projection maintains a checkpoint to track its position in the stream.
/// </para>
/// <para>
/// <strong>Key Concepts:</strong>
/// - Projections are idempotent (can process same event multiple times safely)
/// - Projections can be rebuilt by replaying events from the beginning
/// - Multiple projections can consume the same stream independently
/// - Projections run asynchronously and eventually consistent with the stream
/// </para>
/// </remarks>
public interface IProjection<in TEvent> where TEvent : ICorrelatedEvent
{
/// <summary>
/// Handles an event and updates the read model accordingly.
/// </summary>
/// <param name="event">The event to process.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task representing the async operation.</returns>
/// <remarks>
/// <para>
/// This method should be idempotent - processing the same event multiple times
/// should produce the same result. This is critical for projection rebuilding.
/// </para>
/// <para>
/// If this method throws an exception, the projection engine will retry based on
/// its configured retry policy. Persistent failures may require manual intervention.
/// </para>
/// </remarks>
Task HandleAsync(TEvent @event, CancellationToken cancellationToken = default);
}
/// <summary>
/// Represents a projection that can handle any event type dynamically.
/// </summary>
/// <remarks>
/// Use this interface when you need to handle multiple event types in a single projection
/// or when event types are not known at compile time.
/// </remarks>
public interface IDynamicProjection
{
/// <summary>
/// Handles an event dynamically and updates the read model accordingly.
/// </summary>
/// <param name="event">The event to process.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task representing the async operation.</returns>
Task HandleAsync(ICorrelatedEvent @event, CancellationToken cancellationToken = default);
}
/// <summary>
/// Marker interface for projections that support rebuilding.
/// </summary>
/// <remarks>
/// <para>
/// Projections implementing this interface can be rebuilt from scratch by:
/// 1. Calling ResetAsync() to clear the read model
/// 2. Replaying all events from the beginning
/// 3. Processing events through HandleAsync()
/// </para>
/// <para>
/// This is useful for:
/// - Fixing bugs in projection logic
/// - Schema migrations in the read model
/// - Adding new projections to existing streams
/// </para>
/// </remarks>
public interface IResettableProjection
{
/// <summary>
/// Resets the projection's read model to its initial state.
/// </summary>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task representing the async operation.</returns>
/// <remarks>
/// This method should delete or clear all data in the read model.
/// After calling this, the projection can be rebuilt from offset 0.
/// </remarks>
Task ResetAsync(CancellationToken cancellationToken = default);
}