dotnet-cqrs/Svrnty.CQRS.Events.Abstractions/EventStore/IEventStreamStore.cs

258 lines
11 KiB
C#

using System;
using Svrnty.CQRS.Events.Abstractions.EventStore;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Svrnty.CQRS.Events.Abstractions.Models;
namespace Svrnty.CQRS.Events.Abstractions.EventStore;
/// <summary>
/// Storage abstraction for event streams with message queue semantics.
/// Supports both ephemeral (queue) and persistent (log) stream types.
/// </summary>
/// <remarks>
/// <para>
/// <strong>Ephemeral Streams (Phase 1):</strong>
/// Events are enqueued and dequeued like a message queue. Events are deleted after acknowledgment.
/// Supports multiple consumers with visibility tracking.
/// </para>
/// <para>
/// <strong>Persistent Streams (Phase 2+):</strong>
/// Events are appended to an append-only log. Events are never deleted (except by retention policy).
/// Consumers track their position (offset) in the stream.
/// </para>
/// </remarks>
public interface IEventStreamStore
{
// ========================================================================
// EPHEMERAL STREAM OPERATIONS (Message Queue Semantics)
// ========================================================================
/// <summary>
/// Enqueue an event to an ephemeral stream.
/// </summary>
/// <param name="streamName">The name of the stream.</param>
/// <param name="event">The event to enqueue.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task representing the async operation.</returns>
/// <remarks>
/// For ephemeral streams, this adds the event to a queue.
/// The event will be delivered to consumers and then deleted after acknowledgment.
/// </remarks>
Task EnqueueAsync(
string streamName,
ICorrelatedEvent @event,
CancellationToken cancellationToken = default);
/// <summary>
/// Enqueue multiple events to an ephemeral stream in a batch.
/// </summary>
/// <param name="streamName">The name of the stream.</param>
/// <param name="events">The events to enqueue.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task representing the async operation.</returns>
Task EnqueueBatchAsync(
string streamName,
IEnumerable<ICorrelatedEvent> events,
CancellationToken cancellationToken = default);
/// <summary>
/// Dequeue the next available event from an ephemeral stream for a specific consumer.
/// </summary>
/// <param name="streamName">The name of the stream.</param>
/// <param name="consumerId">The consumer ID requesting the event.</param>
/// <param name="visibilityTimeout">How long the event should be invisible to other consumers while processing.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The next event, or null if the queue is empty.</returns>
/// <remarks>
/// <para>
/// The event becomes invisible to other consumers for the duration of the visibility timeout.
/// The consumer must call <see cref="AcknowledgeAsync"/> to permanently remove the event,
/// or <see cref="NackAsync"/> to make it visible again (for retry).
/// </para>
/// <para>
/// If the visibility timeout expires without acknowledgment, the event automatically becomes
/// visible again for other consumers to process.
/// </para>
/// </remarks>
Task<ICorrelatedEvent?> DequeueAsync(
string streamName,
string consumerId,
TimeSpan visibilityTimeout,
CancellationToken cancellationToken = default);
/// <summary>
/// Acknowledge successful processing of an event, permanently removing it from the queue.
/// </summary>
/// <param name="streamName">The name of the stream.</param>
/// <param name="eventId">The event ID to acknowledge.</param>
/// <param name="consumerId">The consumer ID acknowledging the event.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>True if the event was acknowledged, false if not found or already acknowledged.</returns>
/// <remarks>
/// After acknowledgment, the event is permanently deleted from the ephemeral stream.
/// </remarks>
Task<bool> AcknowledgeAsync(
string streamName,
string eventId,
string consumerId,
CancellationToken cancellationToken = default);
/// <summary>
/// Negative acknowledge (NACK) an event, making it visible again for reprocessing.
/// </summary>
/// <param name="streamName">The name of the stream.</param>
/// <param name="eventId">The event ID to NACK.</param>
/// <param name="consumerId">The consumer ID nacking the event.</param>
/// <param name="requeue">If true, make the event immediately available. If false, send to dead letter queue.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>True if the event was nacked, false if not found.</returns>
/// <remarks>
/// <para>
/// Use NACK when processing fails and the event should be retried.
/// The event becomes immediately visible to other consumers if <paramref name="requeue"/> is true.
/// </para>
/// <para>
/// If <paramref name="requeue"/> is false, the event is moved to a dead letter queue
/// for manual inspection (useful after max retry attempts).
/// </para>
/// </remarks>
Task<bool> NackAsync(
string streamName,
string eventId,
string consumerId,
bool requeue = true,
CancellationToken cancellationToken = default);
/// <summary>
/// Get the approximate count of pending events in an ephemeral stream.
/// </summary>
/// <param name="streamName">The name of the stream.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The approximate number of events waiting to be processed.</returns>
/// <remarks>
/// This count is approximate and may not reflect in-flight events being processed.
/// Use for monitoring and metrics, not for critical business logic.
/// </remarks>
Task<int> GetPendingCountAsync(
string streamName,
CancellationToken cancellationToken = default);
// ========================================================================
// PERSISTENT STREAM OPERATIONS (Event Log Semantics) - Phase 2+
// ========================================================================
/// <summary>
/// Append an event to a persistent stream (append-only log).
/// </summary>
/// <param name="streamName">The name of the stream.</param>
/// <param name="event">The event to append.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The offset (position) assigned to this event in the stream.</returns>
/// <remarks>
/// Phase 2 feature. For persistent streams, events are never deleted (except by retention policies).
/// Events are assigned sequential offsets starting from 0.
/// </remarks>
Task<long> AppendAsync(
string streamName,
ICorrelatedEvent @event,
CancellationToken cancellationToken = default);
/// <summary>
/// Read events from a persistent stream starting at a specific offset.
/// </summary>
/// <param name="streamName">The name of the stream.</param>
/// <param name="fromOffset">The offset to start reading from (inclusive).</param>
/// <param name="maxCount">Maximum number of events to return.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>List of events starting from the specified offset.</returns>
/// <remarks>
/// Phase 2 feature. Used for catch-up subscriptions and event replay.
/// </remarks>
Task<List<ICorrelatedEvent>> ReadStreamAsync(
string streamName,
long fromOffset,
int maxCount,
CancellationToken cancellationToken = default);
/// <summary>
/// Get the current length (number of events) in a persistent stream.
/// </summary>
/// <param name="streamName">The name of the stream.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The total number of events in the stream.</returns>
/// <remarks>
/// Phase 2 feature. Used for monitoring and to detect consumer lag.
/// </remarks>
Task<long> GetStreamLengthAsync(
string streamName,
CancellationToken cancellationToken = default);
/// <summary>
/// Get metadata about a persistent stream.
/// </summary>
/// <param name="streamName">The name of the stream.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Stream metadata including length, retention info, and event timestamps.</returns>
/// <remarks>
/// Phase 2 feature. Provides comprehensive information about stream state for monitoring,
/// consumer lag detection, and retention policy verification.
/// </remarks>
Task<StreamMetadata> GetStreamMetadataAsync(
string streamName,
CancellationToken cancellationToken = default);
// ========================================================================
// CONSUMER OFFSET TRACKING - Phase 6 (Monitoring & Health Checks)
// ========================================================================
/// <summary>
/// Get the current offset (position) of a consumer in a persistent stream.
/// </summary>
/// <param name="streamName">The name of the stream.</param>
/// <param name="consumerId">The consumer ID.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The consumer's current offset, or 0 if no offset is stored.</returns>
/// <remarks>
/// Phase 6 feature. Used for health checks to detect consumer lag and stalled consumers.
/// </remarks>
Task<long> GetConsumerOffsetAsync(
string streamName,
string consumerId,
CancellationToken cancellationToken = default);
/// <summary>
/// Get the last time a consumer updated its offset in a persistent stream.
/// </summary>
/// <param name="streamName">The name of the stream.</param>
/// <param name="consumerId">The consumer ID.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The last update time, or DateTimeOffset.MinValue if no offset is stored.</returns>
/// <remarks>
/// Phase 6 feature. Used for health checks to detect stalled consumers (no progress for extended time).
/// </remarks>
Task<DateTimeOffset> GetConsumerLastUpdateTimeAsync(
string streamName,
string consumerId,
CancellationToken cancellationToken = default);
/// <summary>
/// Update a consumer's offset manually (for management operations).
/// </summary>
/// <param name="streamName">The name of the stream.</param>
/// <param name="consumerId">The consumer ID.</param>
/// <param name="newOffset">The new offset to set.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task representing the async operation.</returns>
/// <remarks>
/// Phase 6 feature. Used by management API to reset consumer positions.
/// Use with caution as this can cause events to be reprocessed or skipped.
/// </remarks>
Task UpdateConsumerOffsetAsync(
string streamName,
string consumerId,
long newOffset,
CancellationToken cancellationToken = default);
}