EventSourcing.Net 0.9.1

dotnet add package EventSourcing.Net --version 0.9.1
                    
NuGet\Install-Package EventSourcing.Net -Version 0.9.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="EventSourcing.Net" Version="0.9.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="EventSourcing.Net" Version="0.9.1" />
                    
Directory.Packages.props
<PackageReference Include="EventSourcing.Net" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add EventSourcing.Net --version 0.9.1
                    
#r "nuget: EventSourcing.Net, 0.9.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package EventSourcing.Net@0.9.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=EventSourcing.Net&version=0.9.1
                    
Install as a Cake Addin
#tool nuget:?package=EventSourcing.Net&version=0.9.1
                    
Install as a Cake Tool

EventSourcing.Net

EventSourcing.Net is a framework inspired by the principles of CQRS and event sourcing architecture.

Key features

  • Built-in snapshots
  • Optimized for high performance with low memory consumption
  • Based on SOLID principles and extensibility
  • Built-in support for JSON and protobuf serialization
  • Full support for MediatR as the command bus

Getting started

Install package from nuget.org

Install-Package EventSourcing.Net

or

dotnet add package EventSourcing.Net

Register in the DI container

Assembly assembly = Assembly.GetExecutingAssembly();  
  
IServiceCollection services = new ServiceCollection();  
services.AddEventSourcing(options =>  
{       
     options.Bus.RegisterCommandHandlers(assembly);
     options.Bus.RegisterEventConsumers(assembly);
});

// get instance of the service provider
IServiceProvider provider = services.BuildServiceProvider();  
  
// start event sourcing engine  
await provider.StartEventSourcingEngine();

Commands

Command can be a simple class/record and should implement interface ICommand:

public record CreateUserCommand(string Name, DateTime BirthDate, string PhoneNumber) : ICommand;

Events

Event is a simple class/record that should implement interface IEvent:

public record UserCreatedEvent(string Name, DateTime BirthDate, string PhoneNumber) : IEvent;

Aggregate

Aggregate is the place where we generating events based on incoming commads. It should inherit from Aggregate<TId, TState, TStateMutator>:

public class UserAggregate : Aggregate<Guid, UserState, UserStateMutator>  
{  
    public UserAggregate(Guid id) : base(id, new UserStateMutator())  
    {  
    }  
  
    public CommandExecutionResult<Guid> CreateUser(ICommandEnvelope<Guid, CreateUserCommand> cmd)  
    {  
        if (!State.IsCreated)  
        {  
            Apply(cmd, new UserCreatedEvent(cmd.Payload.Name, cmd.Payload.BirthDate, cmd.Payload.PhoneNumber));  
        }  
          
        return CommandExecutionResult<Guid>.OkIfChanges(this, cmd);  
    }
}

In the example above aggregate using type Guid as the type of id, UserState as type of state and UserStateMutator as type of state mutator.

In EventSourcing.Net aggregates responds only for processing events and nothing more. Event stream will be loaded by framework and passed to instance of aggregate. At the moment of excecutin CreateUser method state will be exists and available. After each call of Apply state will be changed by mutator with respect to passed event.

State

State is the simple POCO object, that can be serialized to snapshot. It might looks like:

public record UserState  
{  
    /// <summary>  
    /// Property to indicate that current user is exists. Exists means UserCreatedEvent was handled.
    /// </summary>
    public bool IsCreated { get; set; }  
      
    public string Name { get; set; }  
      
    public DateTime BirthDate { get; set; }  
      
    public string PhoneNumber { get; set; }  
}

State mutator

State mutator is the place where incoming events update the state. It should be inherited from StateMutator<TState>

public class UserStateMutator : StateMutator<UserState>  
{  
    /// <summary>  
    /// Default state
    /// </summary>
    public override UserState DefaultState => new UserState()  
    {  
        IsCreated = false
    };  
  
    public UserStateMutator()  
    {  
        // here we register our handlers witch will update the state     
        Register<Guid, UserCreatedEvent>(Handle); 
    }  
      
    private UserState Handle(IEventEnvelope<Guid, UserCreatedEvent> e, UserState state)  
    {  
        state.Name = e.Payload.Name;  
        state.BirthDate = e.Payload.BirthDate;  
        state.PhoneNumber = e.Payload.PhoneNumber;  
        state.IsCreated = true; // mark that current user is created now  
  
        return state;  
    }
}

Command handler

Command handler is the place where the flow starting.

public class UserCommandHandler : CommandHandler<Guid, UserAggregate>  
{  
    public UserCommandHandler() : base(aggregateId => new UserAggregate(aggregateId))  
    {  
    }  
  
    public async Task<ICommandExecutionResult<Guid>> CreateUser(ICommandEnvelope<Guid, CreateUserCommand> cmd, CancellationToken token)  
    {  
        return await Update(cmd, aggregate => aggregate.CreateUser(cmd), token);  
    }
}

Built in bus will call command handlers by convention:

  • Command handler should be inherited from CommandHadler<TId, TAggregate>
  • It should contains methods that accept ICommandEnvelope<TId, TCommand> and optional CancellationToken.
  • It should returns Task<ICommandExecutionResult<TId>>

Event consumers

The final part of the flow is the consumers of the events.

Every consumer should implement one or more interfaces IEventConsumer<TId, TEvent>:

public class UserProjector : IEventConsumer<Guid, UserCreatedEvent>
{  
    public Task Consume(IEventEnvelope<Guid, UserCreatedEvent> envelope)  
    {  
        Console.WriteLine(envelope.Payload);  
        return Task.CompletedTask;  
    }
}

Sending commands

Get the instance of IEventSourcingCommandBus from DI container and use it:

public async Task CreateUser(IEventSourcingCommandBus bus)  
{  
    CreateUserCommand cmd = new CreateUserCommand("Test", new DateTime(2000, 1, 1), "123-456-789");  
    ICommandExecutionResult<Guid> result = await bus.Send(Guid.NewGuid(), cmd);  
}

Extensions

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (5)

Showing the top 5 NuGet packages that depend on EventSourcing.Net:

Package Downloads
EventSourcing.Net.Storage.Postgres

Package Description

EventSourcing.Net.Storage.Redis

Implementation of snapshot store based on redis for EventSourcing.Net

EventSourcing.Net.Bus.Mediatr

Bus implementation based on Mediatr for EventSourcing.Net library.

EventSourcing.Net.Serialization.NewtonsoftJson

This package provides functionality to use Newtonsoft.Json as the serializer for EventSourcing.Net

EventSourcing.Net.Serialization.ProtobufNet

This package provides functionality to use protobuf-net as the serializer for EventSourcing.Net

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.9.1 311 1/5/2024
0.9.0 336 7/7/2023
0.8.4 331 5/12/2023
0.8.3 349 5/9/2023
0.8.2 295 5/7/2023
0.8.1 262 5/4/2023