Overview
Event Store es no solo permite almacenar eventos, sino que también proporciona una forma de publicar y suscribirse a eventos. CodeDesignPlus.Net.EventStore.PubSub
es una biblioteca que implementa CodeDesignPlus.Net.PubSub
para la publicación y suscripción de eventos en aplicaciones .NET Core. Esta biblioteca simplifica el proceso de publicar y suscribirse a eventos, permitiendo a los desarrolladores construir sistemas escalables y desacoplados basados en eventos.
Propósito y Alcance
El propósito de CodeDesignPlus.Net.EventStore.PubSub
es administrar y gestionar la conexión con EventStore para la publicación y suscripción de eventos en aplicaciones .NET Core implementando la interfaz IMessage
de CodeDesignPlus.Net.PubSub
. Esta biblioteca proporciona un conjunto completo de herramientas y servicios para capturar, almacenar y reproducir eventos, facilitando la construcción de sistemas robustos y escalables basados en eventos.
Principales características
- Implementación de la interfaz
IMessage
para la publicación y suscripción de eventos. - Publicación/Suscripción (Pub/Sub): Permite a los servicios suscribirse a eventos específicos y recibir actualizaciones en tiempo real cuando esos eventos se producen.
- Persistencia de Eventos: Los eventos se almacenan de forma duradera, lo que permite un historial completo y auditado de todas las acciones.
- Orden de Eventos: Garantiza que los eventos se procesen en el orden en que fueron escritos, lo cual es crucial para mantener la consistencia.
- Compatibilidad con Proyecciones: Permite crear proyecciones que indexan eventos de múltiples flujos, facilitando la consulta y el análisis.
- Suscripciones Persistentes: Los suscriptores pueden mantener sus conexiones incluso después de reinicios, asegurando que no se pierdan eventos.
- Consumidores Competitivos: Soporta múltiples consumidores que pueden procesar eventos de forma concurrente, asegurando la entrega exactamente una vez.
- Integración con CQRS: Ideal para arquitecturas basadas en Command Query Responsibility Segregation (CQRS), donde las escrituras y lecturas están separadas.
- Seguridad y Control de Acceso: Permite definir permisos y controlar quién puede publicar o suscribirse a eventos.
- Escalabilidad: Diseñado para manejar grandes volúmenes de eventos y suscripciones, ideal para sistemas distribuidos.
Casos de uso típicos
- Historial de Eventos: Ideal para aplicaciones que requieren un historial completo y auditado de eventos.
- CQRS (Command Query Responsibility Segregation): Perfecto para arquitecturas que separan las operaciones de escritura y lectura.
- Proyecciones: Facilita la creación de proyecciones que indexan eventos de múltiples flujos.
- Consumidores Competitivos: Soporta múltiples consumidores que procesan eventos de forma concurrente.
- Persistencia de Eventos: Los eventos se almacenan de forma duradera, asegurando que no se pierdan.
Componentes Principales
EventStorePubSubService
: Implementa la interfazIMessage
para la publicación y suscripción de eventos.EventStorePubSubOptions
: Representa las opciones de configuración para EventStore Pub/Sub.- Métodos de extensión: Facilitan la configuración y el uso de EventStore Pub/Sub en aplicaciones .NET Core.
Directorysrc
DirectoryCodeDesignPlus.Net.EventStore.PubSub
DirectoryExceptions
- EventStorePubSubException.cs
DirectoryExtensions
- ServiceCollectionExtensions.cs
DirectoryServices
- EventStorePubSubService.cs
DirectoryCodeDesignPlus.Net.EventStore.PubSub.Abstractions
- IEventStorePubSub.cs
DirectoryOptions
- EventStorePubSubOptions.cs
Primeros Pasos
En esta sección, aprenderás a instalar y configurar la biblioteca CodeDesignPlus.Net.EventStore.PubSub
en tu proyecto de .NET Core. Además, explorarás los servicios, opciones y métodos de extensión que proporciona la biblioteca para implementar la publicación y suscripción de eventos en aplicaciones .NET Core.
Requisitos previos
- .NET 8 o superior.
- Conocimientos básicos de patrones de diseño y sistemas distribuidos.
- Conocimientos sobre la arquitectura orientada a eventos (Event-Driven).
- Inyección de dependencias en aplicaciones .NET.
Instalación
Para instalar la biblioteca CodeDesignPlus.Net.EventStore.PubSub
, agrega el paquete NuGet a tu proyecto:
dotnet add package CodeDesignPlus.Net.EventStore.PubSub
Install-Package CodeDesignPlus.Net.EventStore.PubSub
<PackageReference Include="CodeDesignPlus.Net.EventStore.PubSub" Version="1.0.0" />
Configuración básica
-
Asignar las opciones de configuración en el
appsettings.json
:{"Core": {"Business": "CodeDesignPlus","AppName": "sample-eventstore-producer","Version": "v1","Description": "Sample of CodeDesignPlus.Net.Core","Contact": {"Name": "CodeDesignPlus","Email": "custom@outlook.com"}},"EventStore": {"Servers": {"Core": {"ConnectionString": "tcp://localhost:1113?tls=false","User": "admin","Password": "12345678"}}},"EventStorePubSub": {"Enabled": true,"UseQueue": false}} -
Registra los servicios el contenedor de dependencias de tu aplicación:
// ...services.AddEventStorePubSub(configuration);
Ejemplo rápido
El proyecto de ejemplo CodeDesignPlus.Net.EventStore.PubSub.Sample
contiene los diferentes casos de uso para hacer uso de la biblioteca.
// See https://aka.ms/new-console-template for more informationusing CodeDesignPlus.Net.EventStore.PubSub.Extensions;using CodeDesignPlus.Net.EventStore.PubSub.Producer.Sample.Aggregates;using CodeDesignPlus.Net.PubSub.Abstractions;using Microsoft.Extensions.Configuration;using Microsoft.Extensions.DependencyInjection;
Console.WriteLine("This is a sample of how to use the CodeDesignPlus.Net.EventStore.PubSub package.");
var configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .Build();
var servicesCollection = new ServiceCollection();servicesCollection.AddLogging();servicesCollection.AddSingleton(configuration);servicesCollection.AddEventStorePubSub(configuration);
var serviceProvider = servicesCollection.BuildServiceProvider();var pubSub = serviceProvider.GetRequiredService<IPubSub>();
// Create an order aggregate and publish the eventsvar orderAggregate = OrderAggregate.Create(Guid.NewGuid(), "Order 1", Guid.NewGuid());
orderAggregate.UpdateName("Order 1 Updated");orderAggregate.AddProduct("Product 1");orderAggregate.AddProduct("Product 2");orderAggregate.AddProduct("Product 3");orderAggregate.AddProduct("Product 4");orderAggregate.AddProduct("Product 5");
foreach (var @event in orderAggregate.GetAndClearEvents()){ _ = pubSub.PublishAsync(@event, CancellationToken.None);
Console.WriteLine($"Event {@event.GetType().Name} published, id: {@event.EventId}, aggregate id: {@event.AggregateId}");}
// Wait for the background services to finishawait Task.Delay(1000);
Console.ReadLine();
-
Configuración de EventStore Pub/Sub.
El archivo
appsettings.json
contiene la configuración de EventStore Pub/Sub.{"Core": {"Business": "CodeDesignPlus","AppName": "sample-eventstore-producer","Version": "v1","Description": "Sample of CodeDesignPlus.Net.Core","Contact": {"Name": "CodeDesignPlus","Email": "custom@outlook.com"}},"EventStore": {"Servers": {"Core": {"ConnectionString": "tcp://localhost:1113?tls=false","User": "admin","Password": "12345678"}}},"EventStorePubSub": {"Enabled": true,"UseQueue": false}} -
Creación del
OrderAggregate
Los eventos de dominio que serán publicados o consumidos, modifican el estado de la aplicación, ya sea un Aggregate o Entidad. En este caso, se crea el
OrderAggregate
que representa una orden.using System;using CodeDesignPlus.Net.Event.Sourcing.Abstractions;using CodeDesignPlus.Net.EventStore.PubSub.Producer.Sample.Events;namespace CodeDesignPlus.Net.EventStore.PubSub.Producer.Sample.Aggregates;public class OrderAggregate : AggregateRoot{public string? Name { get; private set; }public Guid IdUser { get; private set; }public List<string> Products { get; private set; } = [];public override string Category { get; protected set; } = "Order";public OrderAggregate(Guid id) : base(id) { }private OrderAggregate(Guid id, string name, Guid idUser) : base(id){this.Name = name;this.IdUser = idUser;}public static OrderAggregate Create(Guid id, string name, Guid idUser){var aggregate = new OrderAggregate(id, name, idUser);aggregate.AddEvent(new OrderCreatedDomainEvent(id, name, idUser));return aggregate;}public void UpdateName(string name){this.AddEvent(new NameUpdatedDomainEvent(this.Id, name, this.IdUser));}public void AddProduct(string product){this.AddEvent(new ProductAddedDomainEvent(this.Id, product));}private void Apply(OrderCreatedDomainEvent @event){this.Name = @event.Name;this.IdUser = @event.IdUser;}private void Apply(NameUpdatedDomainEvent @event){this.Name = @event.Name;}private void Apply(ProductAddedDomainEvent @event){this.Products.Add(@event.Product);}} -
Creación de los eventos de dominio
Los eventos de dominio representan los cambios en el estado de la aplicación. En este caso, se crean los eventos
OrderCreatedDomainEvent
,NameUpdatedDomainEvent
yProductAddedDomainEvent
.using CodeDesignPlus.Net.Core.Abstractions;using CodeDesignPlus.Net.Core.Abstractions.Attributes;using CodeDesignPlus.Net.EventStore.PubSub.Producer.Sample.Aggregates;namespace CodeDesignPlus.Net.EventStore.PubSub.Producer.Sample.Events;[EventKey<OrderAggregate>(1, "created")]public class OrderCreatedDomainEvent(Guid aggregateId,string name,Guid idUser,Guid? eventId = null,Instant? occurredAt = null,Dictionary<string, object>? metadata = null) : DomainEvent(aggregateId, eventId, occurredAt, metadata){public string Name { get; set; } = name;public Guid IdUser { get; set; } = idUser;}using CodeDesignPlus.Net.Core.Abstractions;using CodeDesignPlus.Net.Core.Abstractions.Attributes;using CodeDesignPlus.Net.EventStore.PubSub.Producer.Sample.Aggregates;namespace CodeDesignPlus.Net.EventStore.PubSub.Producer.Sample.Events;[EventKey<OrderAggregate>(1, "updated")]public class NameUpdatedDomainEvent(Guid aggregateId,string name,Guid? eventId = null,Instant? occurredAt = null,Dictionary<string, object>? metadata = null) : DomainEvent(aggregateId, eventId, occurredAt, metadata){public string Name { get; set; } = name;}using CodeDesignPlus.Net.Core.Abstractions;using CodeDesignPlus.Net.Core.Abstractions.Attributes;using CodeDesignPlus.Net.EventStore.PubSub.Producer.Sample.Aggregates;namespace CodeDesignPlus.Net.EventStore.PubSub.Producer.Sample.Events;[EventKey<OrderAggregate>(1, "product-added")]public class ProductAddedDomainEvent(Guid aggregateId,string product,Guid? eventId = null,Instant? occurredAt = null,Dictionary<string, object>? metadata = null) : DomainEvent(aggregateId, eventId, occurredAt, metadata){public string Product { get; set; } = product;} -
Registraremos los servicios necesarios en el contenedor de dependencias.
services.AddEventStorePubSub(configuration); -
Obtenemos una instancia de
IPubSub
del contenedor de dependencias.var pubSub = host.Services.GetRequiredService<IPubSub>(); -
Creamos la instancia del
OrderAggregate
.Es necesario recordar, que el Aggregate internamente va almacenando los eventos de dominio que se van generando por cada acción que se realice. En este caso se realizan las siguientes acciones:
- Crear la orden: Al crear la instancia del
OrderAggregate
, se crearán los eventosOrderCreatedDomainEvent
. - Modificar el nombre de la orden: Al modificar el nombre de la orden, se crearán los eventos
NameUpdatedDomainEvent
. - Agregar productos a la orden: Al agregar productos a la orden, se crearán los eventos
ProductAddedDomainEvent
.
var orderAggregate = OrderAggregate.Create(Guid.NewGuid(), "Order 1", Guid.NewGuid());orderAggregate.UpdateName("Order 1 Updated");orderAggregate.AddProduct("Product 1");orderAggregate.AddProduct("Product 2");orderAggregate.AddProduct("Product 3");orderAggregate.AddProduct("Product 4");orderAggregate.AddProduct("Product 5"); - Crear la orden: Al crear la instancia del
-
Obtenemos y publicamos los eventos de dominio generados por el
OrderAggregate
.foreach (var @event in orderAggregate.GetAndClearEvents()){_ = pubSub.PublishAsync(@event, CancellationToken.None);Console.WriteLine($"Event {@event.GetType().Name} published, id: {@event.EventId}, aggregate id: {@event.AggregateId}");}
Métodos de extensión
La biblioteca CodeDesignPlus.Net.EventStore.PubSub
proporciona métodos de extensión para facilitar la configuración y el uso de EventStore Pub/Sub en aplicaciones .NET Core. Estos métodos permiten a los desarrolladores integrar EventStore Pub/Sub de manera eficiente y desacoplada, asegurando una gestión robusta y escalable de eventos.
ServiceCollection
ServiceCollection
es una clase que proporciona métodos de extensión para registrar servicios en el contenedor de dependencias.
Opciones de configuración
La biblioteca CodeDesignPlus.Net.EventStore.PubSub
proporciona opciones de configuración para EventStore a través de la clase EventStorePubSubOptions
. Estas opciones permiten a los desarrolladores personalizar la configuración de EventStore, incluidos los detalles del servidor, la autenticación y la configuración de la conexión.
Servicios
La biblioteca CodeDesignPlus.Net.EventStore.PubSub
proporciona varios servicios esenciales, llevando a cabo la implementación del servicio CodeDesignPlus.Net.PubSub.Abstractions.IMessage
para la publicación y suscripción de eventos. Estos servicios están diseñados para facilitar la gestión de eventos y garantizar una comunicación eficiente y desacoplada entre los componentes de la aplicación.
IEventStorePubSub
IEventStorePubSub
hereda de IMessage
, una interfaz base que encapsula las operaciones fundamentales de mensajería como el envío y recepción de mensajes asíncronos. Esta herencia proporciona una base sólida para implementar servicios de publicación/suscripción robustos y escalables.
Conclusiones
CodeDesignPlus.Net.EventStore.PubSub
es una biblioteca robusta y escalable que facilita la implementación de la publicación y suscripción de eventos en aplicaciones .NET Core. Proporciona una serie de servicios, opciones y métodos de extensión que permiten a los desarrolladores construir sistemas desacoplados y eficientes basados en eventos. Con CodeDesignPlus.Net.EventStore.PubSub
, los desarrolladores pueden implementar fácilmente patrones de pub-sub y gestionar eventos de forma eficiente y segura.