using System; using System.Collections.Generic; namespace Svrnty.CQRS.Events.Abstractions.Configuration; /// /// Configuration for external event delivery to cross-service message brokers. /// /// /// This configuration is used to specify how events from a stream should be /// published externally to other services via message brokers like RabbitMQ or Kafka. /// public sealed class ExternalDeliveryConfiguration { /// /// Gets or sets whether external delivery is enabled for this stream. /// /// /// Default: false (events remain internal to the service). /// public bool Enabled { get; set; } = false; /// /// Gets or sets the provider type to use for external delivery. /// /// /// Supported values: "RabbitMQ", "Kafka", "AzureServiceBus", "AwsSns" /// Default: null (must be specified if Enabled = true) /// public string? ProviderType { get; set; } /// /// Gets or sets the connection string for the external message broker. /// /// /// RabbitMQ: amqp://user:pass@localhost:5672/vhost /// Kafka: localhost:9092 /// Azure Service Bus: Endpoint=sb://...;SharedAccessKey=... /// public string? ConnectionString { get; set; } /// /// Gets or sets the exchange name (RabbitMQ) or topic name (Kafka). /// /// /// If not specified, defaults to the stream name. /// Example: "user-service.events" or "orders.events" /// public string? ExchangeName { get; set; } /// /// Gets or sets the exchange type for RabbitMQ. /// /// /// Supported values: "topic", "fanout", "direct", "headers" /// Default: "topic" (recommended for most scenarios) /// public string ExchangeType { get; set; } = "topic"; /// /// Gets or sets the routing key strategy for RabbitMQ. /// /// /// Supported strategies: /// /// EventTypeRoute by event type name (e.g., "UserCreatedEvent") /// StreamNameRoute by stream name (e.g., "user-events") /// CustomUse custom routing key from metadata /// WildcardRoute to all consumers (use "*" routing key) /// /// Default: "EventType" /// public string RoutingKeyStrategy { get; set; } = "EventType"; /// /// Gets or sets whether to automatically declare/create the exchange and queues. /// /// /// Default: true (recommended for development). /// Set to false in production if topology is managed externally. /// public bool AutoDeclareTopology { get; set; } = true; /// /// Gets or sets whether messages should be persistent (survive broker restart). /// /// /// Default: true (durable messages). /// Set to false for fire-and-forget scenarios where message loss is acceptable. /// public bool Persistent { get; set; } = true; /// /// Gets or sets the maximum number of retry attempts for failed publishes. /// /// /// Default: 3 /// Set to 0 to disable retries. /// public int MaxRetries { get; set; } = 3; /// /// Gets or sets the delay between retry attempts. /// /// /// Default: 1 second /// Exponential backoff is applied (delay * 2^attemptNumber). /// public TimeSpan RetryDelay { get; set; } = TimeSpan.FromSeconds(1); /// /// Gets or sets additional provider-specific settings. /// /// /// This allows passing custom configuration to specific providers without /// changing the core configuration model. /// public Dictionary AdditionalSettings { get; set; } = new(); /// /// Validates the configuration. /// /// Thrown if the configuration is invalid. public void Validate() { if (!Enabled) return; if (string.IsNullOrWhiteSpace(ProviderType)) throw new InvalidOperationException("ProviderType must be specified when external delivery is enabled."); if (string.IsNullOrWhiteSpace(ConnectionString)) throw new InvalidOperationException("ConnectionString must be specified when external delivery is enabled."); if (MaxRetries < 0) throw new InvalidOperationException("MaxRetries cannot be negative."); if (RetryDelay <= TimeSpan.Zero) throw new InvalidOperationException("RetryDelay must be positive."); var validExchangeTypes = new[] { "topic", "fanout", "direct", "headers" }; if (!validExchangeTypes.Contains(ExchangeType.ToLowerInvariant())) throw new InvalidOperationException($"ExchangeType must be one of: {string.Join(", ", validExchangeTypes)}"); var validRoutingStrategies = new[] { "EventType", "StreamName", "Custom", "Wildcard" }; if (!validRoutingStrategies.Contains(RoutingKeyStrategy)) throw new InvalidOperationException($"RoutingKeyStrategy must be one of: {string.Join(", ", validRoutingStrategies)}"); } }