IEventStoreConnection
IEventStoreConnection
es una interfaz que define el contrato para conectar e interactuar con una instancia de EventStore. EventStore es una base de datos especializada en el almacenamiento de eventos, utilizada comúnmente en arquitecturas de Event Sourcing y CQRS.
La interfaz se utiliza para abstraer la lógica de conexión y manejo de eventos con EventStore. Permite a los desarrolladores inicializar conexiones, manejar eventos de conexión y desconexión, y gestionar errores y autenticaciones fallidas de manera uniforme y desacoplada del cliente específico de EventStore.
Funcionalidades
-
Inicialización de la Conexión:
Task<ES.IEventStoreConnection> InitializeAsync(Server server)
: Inicializa la conexión a EventStore utilizando los detalles del servidor proporcionados. Este método es asíncrono y devuelve una instancia deIEventStoreConnection
inicializada.
-
Manejo de Eventos:
- Connected: Evento que se dispara cuando la conexión a EventStore se establece correctamente.
- Disconnected: Evento que se dispara cuando la conexión a EventStore se desconecta.
- Reconnecting: Evento que se dispara cuando la conexión a EventStore está intentando reconectarse.
- Closed: Evento que se dispara cuando la conexión a EventStore se cierra.
- ErrorOccurred: Evento que se dispara cuando ocurre un error en la conexión a EventStore.
- AuthenticationFailed: Evento que se dispara cuando la autenticación a EventStore falla.
Características
- Asincronía: La inicialización de la conexión es asíncrona, lo que permite una mejor gestión de recursos y evita bloqueos en el hilo principal.
- Manejo de Eventos: Proporciona eventos para manejar diferentes estados de la conexión, lo que permite a los desarrolladores reaccionar adecuadamente a cambios en el estado de la conexión.
- Abstracción: Al ser una interfaz, permite la implementación de diferentes estrategias de conexión y facilita la prueba unitaria mediante el uso de mocks.
Implementación
La interfaz IEventStoreConnection
se implementa en la clase EventStoreConnection
, que proporciona la lógica para inicializar y gestionar la conexión a EventStore. La clase EventStoreConnection
se encuentra en el espacio de nombres CodeDesignPlus.Net.EventStore.Services
.
namespace CodeDesignPlus.Net.EventStore.Services;
/// <summary>/// Represents a connection to the EventStore./// </summary>/// <remarks>/// This class is responsible for initializing and managing the connection to the EventStore./// </remarks>public class EventStoreConnection : IEventStoreConnection{ private readonly CoreOptions coreOptions; private readonly ILogger<EventStoreConnection> logger;
/// <summary> /// Initializes a new instance of the <see cref="EventStoreConnection"/> class. /// </summary> /// <param name="coreOptions">The core options.</param> /// <param name="logger">The logger instance.</param> /// <exception cref="ArgumentNullException"> /// Thrown when <paramref name="coreOptions"/> or <paramref name="logger"/> is null. /// </exception> public EventStoreConnection(IOptions<CoreOptions> coreOptions, ILogger<EventStoreConnection> logger) { ArgumentNullException.ThrowIfNull(coreOptions); ArgumentNullException.ThrowIfNull(logger);
this.coreOptions = coreOptions.Value; this.logger = logger;
this.logger.LogInformation("EventStoreConnection initialized."); }
/// <summary> /// Initializes the EventStore connection asynchronously. /// </summary> /// <param name="server">The server configuration.</param> /// <returns>The initialized EventStore connection.</returns> /// <exception cref="ArgumentNullException">Thrown when <paramref name="server"/> is null.</exception> public async Task<ES.IEventStoreConnection> InitializeAsync(Server server) { ArgumentNullException.ThrowIfNull(server);
var settings = ES.ConnectionSettings.Create() .SetClusterGossipPort(server.ConnectionString.Port) .DisableTls() .UseConsoleLogger() .KeepReconnecting() .KeepRetrying();
var connection = ES.EventStoreConnection.Create(settings, server.ConnectionString, this.coreOptions.AppName);
connection.Connected += Connected; connection.Disconnected += Disconnected; connection.Reconnecting += Reconnecting; connection.Closed += Closed; connection.ErrorOccurred += ErrorOccurred; connection.AuthenticationFailed += AuthenticationFailed;
await connection.ConnectAsync().ConfigureAwait(false);
this.logger.LogInformation("Successfully connected to EventStore.");
return connection; }
/// <summary> /// Handles the authentication failed event. /// </summary> /// <param name="sender">The event sender.</param> /// <param name="e">The event arguments containing the reason for failure.</param> private void AuthenticationFailed(object sender, ES.ClientAuthenticationFailedEventArgs e) { this.logger.LogError("Authentication failed in EventStore: {Reason}", e.Reason); }
/// <summary> /// Handles the error occurred event. /// </summary> /// <param name="sender">The event sender.</param> /// <param name="e">The event arguments containing the exception.</param> private void ErrorOccurred(object sender, ES.ClientErrorEventArgs e) { this.logger.LogError(e.Exception, "Error occurred in EventStore: {Exception}", e.Exception.Message); }
/// <summary> /// Handles the connection closed event. /// </summary> /// <param name="sender">The event sender.</param> /// <param name="e">The event arguments containing the reason for closure.</param> private void Closed(object sender, ES.ClientClosedEventArgs e) { this.logger.LogInformation("EventStore connection closed: {Reason}", e.Reason); }
/// <summary> /// Handles the reconnecting event. /// </summary> /// <param name="sender">The event sender.</param> /// <param name="e">The event arguments.</param> private void Reconnecting(object sender, ES.ClientReconnectingEventArgs e) { this.logger.LogInformation("Reconnecting to EventStore"); }
/// <summary> /// Handles the disconnected event. /// </summary> /// <param name="sender">The event sender.</param> /// <param name="e">The event arguments.</param> private void Disconnected(object sender, ES.ClientConnectionEventArgs e) { this.logger.LogInformation("EventStore connection disconnected."); }
/// <summary> /// Handles the connected event. /// </summary> /// <param name="sender">The event sender.</param> /// <param name="e">The event arguments.</param> private void Connected(object sender, ES.ClientConnectionEventArgs e) { this.logger.LogInformation("EventStore connection established."); }}
Ejemplo
Una vez que se ha implementado la interfaz IEventStoreConnection
, se puede utilizar para inicializar una conexión a EventStore y manejar eventos de conexión y desconexión.
-
Agregar las propiedades de configuración al archivo
appsettings.json
:{"EventStore": {"Servers": {"Core": {"ConnectionString": "tcp://localhost:1113?tls=false","User": "admin","Password": "12345678"}}}} -
Registrar la interfaz
IEventStoreConnection
en el contenedor de dependencias:public class Startup{public void ConfigureServices(IServiceCollection services){services.AddSingleton<IEventStoreConnection, EventStoreConnection>();services.AddLogging();}} -
Inicializar la conexión a EventStore en una clase de servicio:
public class EventStoreService{private readonly IEventStoreConnection eventStoreConnection;private readonly EventStoreOptions eventStoreOptions;public EventStoreService(IEventStoreConnection eventStoreConnection, IOptions<EventStoreOptions> options){this.eventStoreConnection = eventStoreConnection;this.eventStoreOptions = options.Value;}public async Task SaveEventAsync<TEvent>(TEvent @event){var connection = await eventStoreConnection.InitializeAsync(eventStoreOptions.Servers["Core"]);var eventData = new ES.EventData(Guid.NewGuid(),"eventType",true,Encoding.UTF8.GetBytes(JsonSerializer.Serialize(@event)),null);await connection.AppendToStreamAsync("streamName", ES.ExpectedVersion.Any, eventData);}}
Conclusiones
La interfaz IEventStoreConnection
es esencial para cualquier aplicación que utilice EventStore, proporcionando una manera estructurada y eficiente de manejar conexiones y eventos relacionados. Su implementación permite una gestión robusta de la conexión, asegurando que la aplicación pueda reaccionar adecuadamente a cualquier cambio en el estado de la conexión.