dotnet-cqrs/Svrnty.CQRS.Events/Subscriptions/Subscription.cs

101 lines
3.7 KiB
C#

using System;
using Svrnty.CQRS.Events.Abstractions.Subscriptions;
using System.Collections.Generic;
using Svrnty.CQRS.Events.Abstractions;
namespace Svrnty.CQRS.Events.Subscriptions;
/// <summary>
/// Default implementation of <see cref="ISubscription"/>.
/// Represents a subscription configuration for consuming events from a stream.
/// </summary>
public class Subscription : ISubscription
{
/// <summary>
/// Initializes a new instance of the <see cref="Subscription"/> class.
/// </summary>
/// <param name="subscriptionId">Unique subscription identifier.</param>
/// <param name="streamName">Name of the stream to subscribe to.</param>
/// <param name="mode">Subscription mode (Broadcast, Exclusive, etc.).</param>
public Subscription(string subscriptionId, string streamName, SubscriptionMode mode = SubscriptionMode.Broadcast)
{
if (string.IsNullOrWhiteSpace(subscriptionId))
throw new ArgumentException("Subscription ID cannot be null or whitespace.", nameof(subscriptionId));
if (string.IsNullOrWhiteSpace(streamName))
throw new ArgumentException("Stream name cannot be null or whitespace.", nameof(streamName));
SubscriptionId = subscriptionId;
StreamName = streamName;
Mode = mode;
CreatedAt = DateTimeOffset.UtcNow;
IsActive = true;
VisibilityTimeout = TimeSpan.FromSeconds(30); // Default 30 seconds
}
/// <inheritdoc />
public string SubscriptionId { get; }
/// <inheritdoc />
public string StreamName { get; }
/// <inheritdoc />
public SubscriptionMode Mode { get; set; }
/// <inheritdoc />
public HashSet<string>? EventTypeFilter { get; set; }
/// <inheritdoc />
public bool IsActive { get; set; }
/// <inheritdoc />
public DateTimeOffset CreatedAt { get; }
/// <inheritdoc />
public string? Description { get; set; }
/// <inheritdoc />
public int? MaxConcurrentConsumers { get; set; }
/// <inheritdoc />
public TimeSpan VisibilityTimeout { get; set; }
/// <inheritdoc />
public IReadOnlyDictionary<string, string>? Metadata { get; set; }
// ========================================================================
// Phase 5: Schema Evolution Support
// ========================================================================
/// <inheritdoc />
public bool EnableUpcasting { get; set; }
/// <inheritdoc />
public int? TargetEventVersion { get; set; }
/// <summary>
/// Validates the subscription configuration.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if configuration is invalid.</exception>
public void Validate()
{
if (string.IsNullOrWhiteSpace(SubscriptionId))
throw new InvalidOperationException("Subscription ID cannot be null or whitespace.");
if (string.IsNullOrWhiteSpace(StreamName))
throw new InvalidOperationException("Stream name cannot be null or whitespace.");
if (VisibilityTimeout <= TimeSpan.Zero)
throw new InvalidOperationException($"Visibility timeout must be positive. Got: {VisibilityTimeout}");
if (MaxConcurrentConsumers.HasValue && MaxConcurrentConsumers.Value <= 0)
throw new InvalidOperationException($"MaxConcurrentConsumers must be positive if set. Got: {MaxConcurrentConsumers}");
// Validate mode-specific constraints
if (Mode == SubscriptionMode.ConsumerGroup && MaxConcurrentConsumers.HasValue && MaxConcurrentConsumers.Value == 1)
{
throw new InvalidOperationException(
"ConsumerGroup mode with MaxConcurrentConsumers=1 is inefficient. Use Exclusive mode instead.");
}
}
}