Skip to content

Overview

Event Sourcing es un patrón en el cual los cambios en el estado de la aplicación se almacenan como una secuencia de eventos. En lugar de almacenar el estado actual de una entidad, cada cambio (evento) se almacena, y el estado actual se deriva reproduciendo estos eventos. Este enfoque proporciona un registro completo de auditoría de cambios y permite una recuperación y análisis de datos más flexibles.

Generate Event

Store Event in Event Store

Replay Events

Rebuild Current State

Propósito y alcance


El propósito de CodeDesignPlus.Net.Event.Sourcing es proporcionar una base sólida para implementar Event Sourcing en aplicaciones .NET. La biblioteca está diseñada para simplificar la gestión de eventos de dominio y agregados, permitiendo a los desarrolladores construir sistemas escalables, auditables y flexibles. A través de abstracciones y métodos de extensión, la librería facilita la implementación de clientes como CodeDesignPlus.Net.Event.Store para almacenar eventos en una base de datos.

Principales características


  • Interfaces y Clases Abstractas: Proporciona interfaces y clases base para definir eventos de dominio y agregados.
  • Servicios de Event Sourcing: Incluye servicios para interactuar con un almacén de eventos y gestionar agregados.
  • Modelos y Opciones: Define modelos y opciones de configuración para personalizar el comportamiento de Event Sourcing.
  • Métodos de Extensión: Proporciona métodos de extensión para configurar y utilizar los servicios de Event Sourcing.
  • Contratos y Abstracciones: Define contratos y abstracciones para la impelementación de clientes como CodeDesignPlus.Net.Event.Store.

Casos de uso típicos

  • Desarrollo de sistemas basados en eventos y CQRS.
  • Implementación de Event Sourcing en aplicaciones .NET.
  • Gestión de eventos de dominio y agregados en sistemas distribuidos.
  • Uso de Event Sourcing para auditoría y recuperación de datos.
  • Arquitecturas escalables y mantenibles basadas en eventos.

Componentes Principales---

  • AggregateRoot: Clase base para los agregados raíz en el sistema de Event Sourcing.
  • IEventSourcingService: Interfaz para interactuar con un almacén de eventos en un sistema de Event Sourcing.
  • Metadata: Clase para almacenar metadatos de eventos de dominio.
  • Directorysrc
    • DirectoryCodeDesignPlus.Net.Event.Sourcing
      • DirectoryExceptions
        • EventSourcingException.cs
        • EventSourcingNotImplementedException.cs
      • DirectoryExtensions
        • ServiceCollectionExtensions.cs
    • DirectoryCodeDesignPlus.Net.Event.Sourcing.Abstractions
      • AggregateRoot.cs
      • IAggregateRoot.cs
      • IEventSourcingService.cs
      • Metadata.cs
      • Usings.cs
      • DirectoryOptions
        • EventSourcingOptions.cs

Primeros Pasos


En esta sección, aprenderás a instalar y configurar la librería CodeDesignPlus.Net.Event.Sourcing en tu proyecto de .NET. Además, explorarás los servicios, modelos y métodos de extensión que proporciona la librería para implementar Event Sourcing en aplicaciones .NET.

Requisitos previos

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

Instalación

Para instalar la librería CodeDesignPlus.Net.Event.Sourcing en tu proyecto de .NET, puedes utilizar el administrador de paquetes NuGet o agregar una referencia de paquete en tu archivo .csproj.

Terminal window
dotnet add package CodeDesignPlus.Net.Event.Sourcing

Configuración básica

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

    {
    "Core": {
    "Business": "CodeDesignPlus",
    "AppName": "sample-event-sourcing",
    "Version": "v1",
    "Description": "Sample of CodeDesignPlus.Net.Core",
    "Contact": {
    "Name": "CodeDesignPlus",
    "Email": "custom@outlook.com"
    }
    },
    "EventSourcing": {
    "MainName": "aggregate",
    "SnapshotSuffix": "snapshot",
    "FrequencySnapshot": 20
    }
    }
  2. Registra los servicios el contenedor de dependencias de tu aplicación y La implementación de IEventSourcingService:

    // ...
    servicesCollection.AddEventSourcing(configuration);
    servicesCollection.AddSingleton<IEventSourcingService, InMemoryEventSourcingService>();

Ejemplo rápido


El proyecto de ejemplo CodeDesignPlus.Net.Event.Sourcing.Sample expone como hacer uso de la librería.

// See https://aka.ms/new-console-template for more information
using CodeDesignPlus.Net.Event.Sourcing.Abstractions;
using CodeDesignPlus.Net.Event.Sourcing.Extensions;
using CodeDesignPlus.Net.Event.Sourcing.Sample.Aggregates;
using CodeDesignPlus.Net.Event.Sourcing.Sample.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
Console.WriteLine("This is a sample of how to use the library CodeDesignPlus.Net.Event.Sourcing");
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.Build();
var servicesCollection = new ServiceCollection();
servicesCollection.AddEventSourcing(configuration);
servicesCollection.AddSingleton<IEventSourcing, InMemoryEventSourcingService>();
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");
var events = orderAggregate.GetAndClearEvents();
var orderRehidrated = OrderAggregate.Rehydrate<OrderAggregate>(Guid.NewGuid(), events);
Console.WriteLine($"Order Id: {orderRehidrated.Id}");
  1. Configuración appsettings

    El archivo appsettings.json contiene la configuración necesaria para la librería:

    {
    "Core": {
    "Business": "CodeDesignPlus",
    "AppName": "Net Core Sample",
    "Version": "v1",
    "Description": "Sample of CodeDesignPlus.Net.Core",
    "Contact": {
    "Name": "CodeDesignPlus",
    "Email": "custom@outlook.com"
    }
    },
    "EventSourcing": {
    "MainName": "aggregate",
    "SnapshotSuffix": "snapshot",
    "FrequencySnapshot": 20
    }
    }
  2. Implementación del Aggregate

    La clase OrderAggregate representa un agregado raíz en el sistema de Event Sourcing. Esta clase hereda de AggregateRoot y define propiedades como Name, IdUser y Products. Además, define métodos como Create, UpdateName y AddProduct para modificar el estado del agregado y aplicar eventos de dominio.

    using CodeDesignPlus.Net.Event.Sourcing.Abstractions;
    using CodeDesignPlus.Net.Event.Sourcing.Sample.Events;
    namespace CodeDesignPlus.Net.Event.Sourcing.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);
    }
    }
  3. Implementación de los Eventos de Dominio

    Se definen eventos de dominio como NameUpdatedDomainEvent, OrderCreatedDomainEvent y ProductAddedDomainEvent que representan cambios en el estado del agregado. Estos eventos heredan de DomainEvent y definen propiedades como Name, IdUser y Product para almacenar información relevante.

    using CodeDesignPlus.Net.Core.Abstractions;
    using CodeDesignPlus.Net.Core.Abstractions.Attributes;
    using CodeDesignPlus.Net.Event.Sourcing.Sample.Aggregates;
    namespace CodeDesignPlus.Net.Event.Sourcing.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.Event.Sourcing.Sample.Aggregates;
    namespace CodeDesignPlus.Net.Event.Sourcing.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.Event.Sourcing.Sample.Aggregates;
    namespace CodeDesignPlus.Net.Event.Sourcing.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;
    }
  4. Implementación de InMemoryEventSourcingService.cs

    Este es un ejemplo Fake para el servicio de Event Sourcing, esto se debe a CodeDesignPlus.Net.Event.Store tiene una implementación real para almacenar eventos en la bsae de datos Event Store.

    using System;
    using CodeDesignPlus.Net.Core.Abstractions;
    using CodeDesignPlus.Net.Event.Sourcing.Abstractions;
    namespace CodeDesignPlus.Net.Event.Sourcing.Sample.Services;
    public class InMemoryEventSourcingService : IEventSourcing
    {
    public Task AppendEventAsync<TDomainEvent>(string category, TDomainEvent @event, long? version = null, CancellationToken cancellationToken = default) where TDomainEvent : IDomainEvent
    {
    throw new NotImplementedException();
    }
    public Task<long> CountEventsAsync(string category, Guid aggregateId, CancellationToken cancellationToken = default)
    {
    throw new NotImplementedException();
    }
    public Task<long> GetVersionAsync(string category, Guid aggregateId, CancellationToken cancellationToken = default)
    {
    throw new NotImplementedException();
    }
    public Task<IEnumerable<IDomainEvent>> LoadEventsAsync(string category, Guid aggregateId, CancellationToken cancellationToken = default)
    {
    throw new NotImplementedException();
    }
    public Task<TAggregate> LoadSnapshotAsync<TAggregate>(string category, Guid aggregateId, CancellationToken cancellationToken = default) where TAggregate : Abstractions.IAggregateRoot
    {
    throw new NotImplementedException();
    }
    public Task SaveSnapshotAsync<TAggregate>(TAggregate aggregate, CancellationToken cancellationToken = default) where TAggregate : Abstractions.IAggregateRoot
    {
    throw new NotImplementedException();
    }
    public Task<IEnumerable<IDomainEvent>> SearchEventsAsync(string streamName, CancellationToken cancellationToken = default)
    {
    throw new NotImplementedException();
    }
    public Task<IEnumerable<TDomainEvent>> SearchEventsAsync<TDomainEvent>(CancellationToken cancellationToken = default) where TDomainEvent : IDomainEvent
    {
    throw new NotImplementedException();
    }
    public Task<IEnumerable<TDomainEvent>> SearchEventsAsync<TDomainEvent>(string category, CancellationToken cancellationToken = default) where TDomainEvent : IDomainEvent
    {
    throw new NotImplementedException();
    }
    }
  5. Registrar Servicios

    Se crea una colección de servicios y se agrega la configuración de event sourcing. Además, se registra InMemoryEventSourcingService como la implementación de IEventSourcingService.

    var servicesCollection = new ServiceCollection();
    servicesCollection.AddEventSourcing(configuration);
    servicesCollection.AddSingleton<IEventSourcingService, InMemoryEventSourcingService>();
  6. Obtención del Servicio de Event Sourcing:

    Se obtiene una instancia del servicio de event sourcing desde el proveedor de servicios.

    var eventSourcing = serviceProvider.GetRequiredService<IEventSourcingService>();
  7. Creación del Agregado:

    Se crea una nueva instancia de OrderAggregate con un identificador único, un nombre y un identificador de usuario.

    var orderAggregate = OrderAggregate.Create(Guid.NewGuid(), "Order 1", Guid.NewGuid());
  8. Actualización del estado del Aggregate

    Procedemos a modificar el estado del agregado para obtener los eventos y así podamos usar el proceso de rehidratación en los próximos pasos.

    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");
  9. Obtención y Limpieza de Eventos:

    Se obtienen y limpian los eventos generados por el agregado.

    var events = orderAggregate.GetAndClearEvents();
  10. Rehidratación del Agregado:

    Se rehidrata un nuevo agregado a partir de los eventos obtenidos, utilizando un nuevo identificador único.

    var orderRehidrated = OrderAggregate.Rehydrate<OrderAggregate>(Guid.NewGuid(), events);

Métodos de extensión


La librería CodeDesignPlus.Net.Event.Sourcing proporciona una serie de métodos de extensión para configurar y utilizar los servicios de Event Sourcing en una aplicación .NET. Estos métodos de extensión están diseñados para integrarse fácilmente con el contenedor de dependencias de .NET y simplificar la configuración de los servicios de Event Sourcing.

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.Event.Sourcing proporciona opciones de configuración estructuradas y fácilmente accesibles. Estas opciones están definidas en el espacio de nombres CodeDesignPlus.Net.Event.Sourcing.Abstractions.Options.

Servicios


La biblioteca CodeDesignPlus.Net.Event.Sourcing proporciona varios servicios para interactuar con un almacén de eventos en un sistema de Event Sourcing. Estos servicios están definidos en el espacio de nombres CodeDesignPlus.Net.Event.Sourcing.Abstractions.

IEventSourcingService

Esta interfaz proporciona operaciones para interactuar con un almacén de eventos. Incluye métodos para agregar eventos, cargar eventos, contar eventos y gestionar snapshots.

AggregateRoot

Esta clase abstracta representa la clase base para los agregados raíz en el sistema de Event Sourcing. Proporciona métodos para aplicar eventos, agregar eventos y rehidratar el agregado a partir de eventos de dominios.

Models


La biblioteca CodeDesignPlus.Net.Event.Sourcing proporciona una serie de modelos que son utilizados en el sistema de Event Sourcing. Estos modelos están definidos en el espacio de nombres CodeDesignPlus.Net.Event.Sourcing.Abstractions.

Metadata

Esta clase representa los metadatos del evento. Incluye propiedades como el ID del agregado, la versión, el ID del usuario y la categoría y es usada por el AggregateRoot para adicionar metadatos a los eventos.

namespace CodeDesignPlus.Net.Event.Sourcing.Abstractions;
/// <summary>
/// Represents the metadata of the event.
/// </summary>
public class Metadata
{
/// <summary>
/// Initializes a new instance of the <see cref="Metadata"/> class.
/// </summary>
/// <param name="aggregateId">The identifier of the aggregate root that generated the event.</param>
/// <param name="version">The version of the aggregate root.</param>
/// <param name="userId">The identifier of the user who made the change.</param>
/// <param name="category">The category of the aggregate root.</param>
public Metadata(Guid aggregateId, long version, Guid userId, string category)
{
AggregateId = aggregateId;
Version = version;
UserId = userId;
Category = category;
}
/// <summary>
/// Gets the identifier of the aggregate root that generated the event.
/// </summary>
public Guid AggregateId { get; private set; }
/// <summary>
/// Gets the version of the aggregate root.
/// </summary>
public long Version { get; private set; }
/// <summary>
/// Gets the identifier of the user who made the change.
/// </summary>
public Guid UserId { get; private set; }
/// <summary>
/// Gets the category of the aggregate root.
/// </summary>
public string Category { get; private set; }
}

Conclusiones


En esta guía, aprendiste a instalar y configurar la librería CodeDesignPlus.Net.Event.Sourcing en tu proyecto de .NET. Exploraste los servicios, modelos y métodos de extensión que proporciona la librería para implementar Event Sourcing en aplicaciones .NET. Además, viste un ejemplo de uso de la librería para gestionar eventos de dominio y agregados en una aplicación .NET.

Recursos adicionales