Skip to content

Overview

El Event Sourcing es un patrón de diseño en el que los cambios en el estado de una aplicación se almacenan como una secuencia de eventos. En lugar de almacenar solo el estado actual de los datos, cada cambio se registra como un evento que describe la modificación. Esto permite reconstruir el estado actual reproduciendo todos los eventos en orden. Event Sourcing es especialmente útil en sistemas financieros, aplicaciones de auditoría, sistemas de control de versiones, aplicaciones de comercio electrónico y sistemas distribuidos.

La biblioteca CodeDesignPlus.Net.EventStore proporciona una solución completa y eficiente para la implementación de event sourcing en aplicaciones .NET utilizando EventStore. La biblioteca incluye servicios, opciones de configuración y métodos de extensión para facilitar la integración con EventStore y garantizar una gestión robusta y escalable de eventos.

Propósito y alcance


El objetivo de la biblioteca CodeDesignPlus.Net.EventStore es proporcionar una solución completa y eficiente para la implementación de event sourcing en aplicaciones .NET implementando los servicios definidos en en la biblioteca CodeDesignPlus.Net.Event.Sourcing y asi facilitar la integración con EventStore.

Principales características


  • Servicios: Incluye servicios esenciales para la gestión de eventos, flujos de eventos y conexiones a EventStore.
  • Opciones de Configuración: Proporciona opciones de configuración para personalizar la configuración de EventStore.
  • Métodos de Extensión: Ofrece métodos de extensión útiles para interactuar con EventStore de manera eficiente.

Casos de uso típicos


  • Sistemas de Auditoría: Donde es necesario mantener un historial completo de cambios.
  • Sistemas Financieros: Para rastrear transacciones y cambios en el estado.
  • Aplicaciones de Comercio Electrónico: Para mantener un historial de pedidos y cambios en el inventario.
  • Sistemas de Control de Versiones: Para rastrear cambios en los datos y mantener un historial de versiones.
  • Gestión de eventos de dominio: Para capturar, almacenar y gestionar eventos de dominio en aplicaciones .NET.

Componentes principales


  • EventStoreConnection: Clase que implementa la interfaz IEventStoreConnection y se encarga de conectar e interactuar con una instancia de EventStore.
  • EventStoreFactory: Clase que implementa la interfaz IEventStoreFactory y se encarga de gestionar y recuperar conexiones a instancias de EventStore.
  • EventStoreService: Clase que implementa la interfaz CodeDesignPlus.Net.Event.Sourcing.Abstractions.IEventSourcingService.cs que proporciona métodos para gestionar eventos de manera eficiente independientemente del proveedor de almacenamiento de eventos.
  • EventStoreOptions: Clase que define las opciones de configuración para EventStore, incluyendo detalles del servidor, autenticación y configuración de la conexión.
  • Directorysrc
    • DirectoryCodeDesignPlus.Net.EventStore
      • DirectoryExceptions
        • EventStoreException.cs
      • DirectoryExtensions
        • ServiceCollectionExtensions.cs
      • DirectorySerializer
        • EventStoreContratResolver.cs
      • DirectoryServices
        • EventStoreConnection.cs
        • EventStoreFactory.cs
        • EventStoreService.cs
    • DirectoryCodeDesignPlus.Net.EventStore.Abstractions
      • EventStoreFactoryConst.cs
      • IEventStoreConnection.cs
      • IEventStoreFactory.cs
      • IEventStoreService.cs
      • Server.cs
      • DirectoryOptions
        • EventStoreOptions.cs

Primeros pasos


En esta sección, aprenderás a configurar y utilizar la biblioteca CodeDesignPlus.Net.EventStore en tus aplicaciones .NET. A continuación, se describen los pasos necesarios para integrar EventStore en tu aplicación y comenzar a gestionar eventos de manera eficiente.

Requisitos previos

  • .NET Core 8 o superior.
  • Conocimientos básicos de Event Sourcing y CQRS.
  • Inyección de dependencias en aplicaciones .NET.

Instalación

Para instalar la biblioteca CodeDesignPlus.Net.EventStore, puedes utilizar el administrador de paquetes NuGet o la CLI de .NET. A continuación, se muestran los comandos para instalar la biblioteca utilizando NuGet y la CLI de .NET.

Terminal window
dotnet add package CodeDesignPlus.Net.EventStore

Configuración básica

  1. Asignar las opciones de configuración en el appsettings.json:

    {
    "Core": {
    "Business": "CodeDesignPlus",
    "AppName": "sample-eventstore",
    "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"
    }
    }
    }
    }
  2. Registra los servicios en el contenedor de dependencias de tu aplicación:

    // ...
    servicesCollection.AddEventStore(configuration);

Ejemplo rápido


El proyecto de ejemplo CodeDesignPlus.Net.EventStore.Sample hace uso de la biblioteca CodeDesignPlus.Net.EventStore para gestionar eventos de manera eficiente.

// See https://aka.ms/new-console-template for more information
using CodeDesignPlus.Net.Event.Sourcing.Abstractions;
using CodeDesignPlus.Net.EventStore.Extensions;
using CodeDesignPlus.Net.EventStore.Sample.Aggregates;
using CodeDesignPlus.Net.EventStore.Sample.Events;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
Console.WriteLine("This is a sample of how to use the CodeDesignPlus.Net.EventStore 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.AddEventStore(configuration);
var serviceProvider = servicesCollection.BuildServiceProvider();
var eventSourcing = serviceProvider.GetRequiredService<IEventSourcing>();
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");
// AppendEventAsync - Append a new event to the event store
foreach (var @event in orderAggregate.GetAndClearEvents())
{
await eventSourcing.AppendEventAsync("Order", @event);
}
// CountEventAsync - Count the number of events in the event store
var countEvents = await eventSourcing.CountEventsAsync("Order", orderAggregate.Id);
Console.WriteLine($"Count events: {countEvents}");
// GetVersionAsync - Get the version of the aggregate
var version = await eventSourcing.GetVersionAsync("Order", orderAggregate.Id);
Console.WriteLine($"Version Aggregate: {orderAggregate.Version} - Version Event Store: {version}");
// LoadEventsAsync - Load all events of the aggregate
var events = await eventSourcing.LoadEventsAsync("Order", orderAggregate.Id);
var orderRehydrate = AggregateRoot.Rehydrate<OrderAggregate>(orderAggregate.Id, events);
Console.WriteLine($"Order rehydrate: {orderRehydrate.Name}");
// SaveSnapshotAsync - Save a snapshot of the aggregate
await eventSourcing.SaveSnapshotAsync(orderAggregate);
// LoadSnapshotAsync - Load the snapshot of the aggregate
var orderSnapshot = await eventSourcing.LoadSnapshotAsync<OrderAggregate>("Order", orderAggregate.Id);
Console.WriteLine($"Order snapshot: {orderSnapshot.Name}");
// SearchEventsAsync - Search all events of the event store
var allEvents = await eventSourcing.SearchEventsAsync($"Order-{orderAggregate.Id}");
foreach (var @event in allEvents)
{
Console.WriteLine($"Event: {@event}");
}
// SearchEventsAsync - Search all events of the event store by type
var allEventsByType = await eventSourcing.SearchEventsAsync<ProductAddedDomainEvent>();
foreach (var @event in allEventsByType)
{
Console.WriteLine($"Event: {@event}");
}
// SearchEventsAsync - Search all events of the event store by category
var allEventsByCategory = await eventSourcing.SearchEventsAsync<ProductAddedDomainEvent>("Order");
foreach (var @event in allEventsByCategory)
{
Console.WriteLine($"Event: {@event}");
}
  1. Configuración de EventStore en el appsettings.json:

    El archivo appsettings.json contiene la configuración de EventStore, incluyendo los detalles del servidor, la autenticación y la configuración de la conexión.

    {
    "Core": {
    "Business": "CodeDesignPlus",
    "AppName": "sample-eventstore",
    "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"
    }
    }
    }
    }
  2. Creación de los eventos de dominio:

    Las clases NameUpdatedDomainEvent, OrderCreatedDomainEvent y ProductAddedDomainEvent representan eventos de dominio que se utilizan para gestionar eventos en la aplicación.

    using CodeDesignPlus.Net.Core.Abstractions;
    using CodeDesignPlus.Net.Core.Abstractions.Attributes;
    using CodeDesignPlus.Net.EventStore.Sample.Aggregates;
    namespace CodeDesignPlus.Net.EventStore.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.Sample.Aggregates;
    namespace CodeDesignPlus.Net.EventStore.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.Sample.Aggregates;
    namespace CodeDesignPlus.Net.EventStore.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;
    }
  3. Creación del OrderAggregate

    La clase OrderAggregate representa un agregado de eventos que se utiliza para gestionar eventos de pedidos.

    using System;
    using CodeDesignPlus.Net.Event.Sourcing.Abstractions;
    using CodeDesignPlus.Net.EventStore.Sample.Events;
    namespace CodeDesignPlus.Net.EventStore.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);
    }
    }
  4. Registrar Servicios

    Se registran los servicios de EventStore en el contenedor de dependencias de la aplicación.

    var servicesCollection = new ServiceCollection();
    servicesCollection.AddLogging();
    servicesCollection.AddSingleton(configuration);
    servicesCollection.AddEventStore(configuration);
  5. Uso del servicio IEventSourcingService

    Se obtiene una instancia del servicio IEventSourcingService del proveedor de servicios.

    var eventSourcing = serviceProvider.GetRequiredService<IEventSourcingService>();
  6. Creación y manipulación del OrderAggregate

    Se crea una instancia de OrderAggregate y se manipula utilizando el servicio IEventSourcingService.

    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");
    foreach (var @event in orderAggregate.GetAndClearEvents())
    {
    await eventSourcing.AppendEventAsync("Order", @event);
    }

Métodos de extensión


La biblioteca CodeDesignPlus.Net.EventStore proporciona una serie de métodos de extensión útiles para simplificar la implementación de EventStore en aplicaciones .NET. Estos métodos permiten a los desarrolladores interactuar con EventStore de manera eficiente, realizar operaciones comunes y gestionar eventos de forma sencilla.

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 proporciona opciones de configuración para EventStore a través de la clase EventStoreOptions. 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 proporciona varios servicios esenciales para la implementación de event sourcing. Estos servicios se encuentran en la carpeta CodeDesignPlus.Net.EventStore.Abstractions.

IEventStoreService

Este servicio hereda de IEventSourcingService y proporciona métodos adicionales específicos de EventStore para gestionar eventos de manera eficiente.

IEventStoreFactory

Este servicio define un contrato para gestionar y recuperar conexiones a instancias de EventStore de manera eficiente.

IEventStoreConnection

Este servicio define un contrato para conectar e interactuar con una instancia de EventStore.

Conclusiones


La biblioteca CodeDesignPlus.Net.EventStore proporciona una solución completa y eficiente para la implementación de event sourcing en aplicaciones .NET utilizando EventStore. La biblioteca incluye servicios, opciones de configuración y métodos de extensión para facilitar la integración con EventStore y garantizar una gestión robusta y escalable de eventos.

Recursos adicionales