IEventStoreFactory
IEventStoreFactory
es una interfaz que define un contrato para gestionar y recuperar conexiones a instancias de EventStore. Esta interfaz permite la creación, almacenamiento en caché y gestión eficiente de conexiones a EventStore.
Se utiliza para abstraer la lógica de creación y gestión de conexiones a EventStore. Proporciona métodos para crear nuevas conexiones, recuperar conexiones existentes y eliminar conexiones cuando ya no son necesarias.
Funcionalidades
-
Creación de Conexiones:
Task<ES.IEventStoreConnection> CreateAsync(string key, CancellationToken cancellationToken = default)
: Crea o recupera una conexión existente a EventStore basada en la clave proporcionada. Este método es asíncrono y devuelve una instancia deIEventStoreConnection
inicializada.
-
Eliminación de Conexiones:
bool RemoveConnection(string key)
: Intenta eliminar la conexión con la clave especificada. Devuelvetrue
si la conexión se eliminó correctamente; de lo contrario, devuelvefalse
.
-
Obtención de Credenciales:
(string, string) GetCredentials(string key)
: Obtiene las credenciales (nombre de usuario y contraseña) para la clave de conexión especificada. Lanza una excepción si la clave es nula o vacía, o si la clave no se encuentra en la configuración.
Beneficios del Patrón Factory
El patrón Factory proporciona varios beneficios al implementar IEventStoreFactory
:
- Desacoplamiento: Separa la lógica de creación de objetos de su uso, permitiendo que las clases dependientes no necesiten conocer los detalles de la creación de instancias.
- Reutilización de Conexiones: Permite la reutilización de conexiones existentes, mejorando la eficiencia y reduciendo la sobrecarga de recursos.
- Gestión Centralizada: Centraliza la lógica de creación y gestión de conexiones, facilitando el mantenimiento y la actualización del código.
- Facilidad de Pruebas: Facilita la creación de pruebas unitarias mediante el uso de mocks y stubs, ya que las dependencias pueden ser inyectadas y controladas fácilmente.
Dependencias
IEventStoreFactory
depende de IEventStoreConnection
para inicializar y gestionar las conexiones a EventStore. La interfaz IEventStoreConnection
define el contrato para conectar e interactuar con una instancia de EventStore, mientras que IEventStoreFactory
gestiona la creación y el ciclo de vida de estas conexiones.
Implementación
La interfaz IEventStoreFactory
se implementa en la clase EventStoreFactory
, que proporciona la lógica para gestionar y recuperar conexiones a instancias de EventStore. La clase EventStoreFactory
se encuentra en el espacio de nombres CodeDesignPlus.Net.EventStore.Services
.
namespace CodeDesignPlus.Net.EventStore.Services;
/// <summary>/// Provides a factory to manage and retrieve connections to EventStore instances./// This class ensures that connections are created, cached, and managed efficiently./// </summary>public class EventStoreFactory : IEventStoreFactory{ private readonly ConcurrentDictionary<string, ES.IEventStoreConnection> connections = new(); private readonly IEventStoreConnection eventStoreConnection; private readonly ILogger<EventStoreFactory> logger; private readonly EventStoreOptions options;
/// <summary> /// Initializes a new instance of the <see cref="EventStoreFactory"/> class. /// </summary> /// <param name="eventStoreConnection">The handler to initialize EventStore connections.</param> /// <param name="logger">The logger used for logging events and issues related to the EventStore connections.</param> /// <param name="options">The configuration options for the EventStore connections.</param> /// <exception cref="ArgumentNullException"> /// Thrown when <paramref name="eventStoreConnection"/>, <paramref name="logger"/>, or <paramref name="options"/> is null. /// </exception> public EventStoreFactory(IEventStoreConnection eventStoreConnection, ILogger<EventStoreFactory> logger, IOptions<EventStoreOptions> options) { ArgumentNullException.ThrowIfNull(options); ArgumentNullException.ThrowIfNull(eventStoreConnection); ArgumentNullException.ThrowIfNull(logger);
this.eventStoreConnection = eventStoreConnection; this.logger = logger; this.options = options.Value; }
/// <summary> /// Creates or retrieves an existing connection to EventStore based on the provided key. /// </summary> /// <param name="key">The unique identifier representing the desired connection.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <returns>A task representing the asynchronous operation. The task result contains the connection to EventStore.</returns> /// <exception cref="ArgumentNullException">Thrown when <paramref name="key"/> is null or empty.</exception> /// <exception cref="EventStoreException">Thrown when the specified connection key is not found in the settings.</exception> public async Task<ES.IEventStoreConnection> CreateAsync(string key, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key));
if (!options.Servers.TryGetValue(key, out Server server)) throw new EventStoreException("The connection is not registered in the settings.");
if (connections.TryGetValue(key, out ES.IEventStoreConnection connection)) return connection;
connection = await this.eventStoreConnection.InitializeAsync(server).ConfigureAwait(false); var success = this.connections.TryAdd(key, connection);
this.logger.LogInformation("Successfully created and cached the connection for key '{Key}-{Success}'.", key, success);
return connection; }
/// <summary> /// Attempts to remove the connection with the specified key. /// </summary> /// <param name="key">The unique identifier for the connection to be removed.</param> /// <returns><c>true</c> if the connection was successfully removed; otherwise, <c>false</c>.</returns> public bool RemoveConnection(string key) { var result = this.connections.TryRemove(key, out _);
if (result) this.logger.LogInformation("Successfully removed the connection for key '{Key}'.", key); else this.logger.LogWarning("Failed to remove the connection for key '{Key}'.", key);
return result; }
/// <summary> /// Gets the credentials for the specified connection key. /// </summary> /// <param name="key">The key connection.</param> /// <returns>A tuple containing the username and password for the specified connection key.</returns> /// <exception cref="ArgumentNullException">Thrown when <paramref name="key"/> is null or empty.</exception> /// <exception cref="EventStoreException">Thrown when the specified connection key is not found in the settings.</exception> public (string, string) GetCredentials(string key) { if (string.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key));
if (!options.Servers.TryGetValue(key, out Server server)) throw new EventStoreException("The connection is not registered in the settings.");
return (server.User, server.Password); }}
Ejemplo
Una vez que se ha implementado la interfaz IEventStoreFactory
, se puede utilizar para crear y gestionar conexiones a EventStore en una aplicación .NET.
-
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
IEventStoreFactory
en el contenedor de dependencias:public class Startup{public void ConfigureServices(IServiceCollection services){services.AddSingleton<IEventStoreConnection, EventStoreConnection>();services.AddSingleton<IEventStoreFactory, EventStoreFactory>();services.AddLogging();}} -
Utilizar la fábrica para crear y gestionar conexiones a EventStore en la aplicación.
public class EventStoreService{private readonly IEventStoreFactory eventStoreFactory;public EventStoreService(IEventStoreFactory eventStoreFactory){this.eventStoreFactory = eventStoreFactory;}public async Task SaveEventAsync<TEvent>(TEvent @event, string key){var connection = await eventStoreFactory.CreateAsync(key);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 IEventStoreFactory
es esencial para cualquier aplicación que utilice EventStore, proporcionando una manera estructurada y eficiente de gestionar conexiones. Implementar el patrón Factory trae beneficios significativos, como el desacoplamiento, la reutilización de conexiones, la gestión centralizada y la facilidad de pruebas. Además, la relación con IEventStoreConnection
asegura que las conexiones se inicialicen y gestionen de manera robusta, proporcionando una capa de abstracción que facilita el uso y la prueba de la conexión en aplicaciones que utilizan EventStore.