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.
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
.
dotnet add package CodeDesignPlus.Net.Event.Sourcing
Install-Package CodeDesignPlus.Net.Event.Sourcing
<PackageReference Include="CodeDesignPlus.Net.Event.Sourcing" Version="1.0.0" />
Configuración básica
-
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}} -
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 informationusing 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}");
-
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}} -
Implementación del Aggregate
La clase
OrderAggregate
representa un agregado raíz en el sistema de Event Sourcing. Esta clase hereda deAggregateRoot
y define propiedades comoName
,IdUser
yProducts
. Además, define métodos comoCreate
,UpdateName
yAddProduct
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);}} -
Implementación de los Eventos de Dominio
Se definen eventos de dominio como
NameUpdatedDomainEvent
,OrderCreatedDomainEvent
yProductAddedDomainEvent
que representan cambios en el estado del agregado. Estos eventos heredan deDomainEvent
y definen propiedades comoName
,IdUser
yProduct
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;} -
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();}} -
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 deIEventSourcingService
.var servicesCollection = new ServiceCollection();servicesCollection.AddEventSourcing(configuration);servicesCollection.AddSingleton<IEventSourcingService, InMemoryEventSourcingService>(); -
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>(); -
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()); -
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"); -
Obtención y Limpieza de Eventos:
Se obtienen y limpian los eventos generados por el agregado.
var events = orderAggregate.GetAndClearEvents(); -
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.