Event Sourcing made simple using Reactive Extensions
Simple Event Sourcing is a .NET library based on the Event Sourcing architecture. This library is written with Rx.NET and built with the minimum of code you need to create event sourced application.
There is a sample application in this repository. It is a console application that showcases how you can use this library in your backend application. The console application focuses on:
Simple Event Sourcing library is defined by 5 different components:
Commands are the actions coming from the world, generally from an HTTP request. It is the intent of a user or of the system to mutate the data store.
public class AddItemInCartCommand
{
public long ItemId { get; set; }
public int Quantity { get; set; }
}
public class RemoveItemFromCartCommand
{
public long ItemId { get; set; }
public int Quantity { get; set; }
}
public class ResetCartCommand { }
The CommandDispatcher
is the place where you dispatch all actions/commands that come from the world. A command can generate multiple events.
public abstract class CommandDispatcher<TCommand, TEvent>
where TCommand : class, new()
where TEvent : SimpleEvent
{
public void Dispatch(TCommand command);
public IObservable<IEnumerable<TEvent>> ObserveEventAggregate();
}
Events are your own definition of the business actions. Using this library, you have to express each event as class
and we take care of the rest. Here are some examples of events:
public class CartItemSelected
{
public long ItemId { get; set; }
public int Quantity { get; set; }
}
public class CartItemUnselected
{
public long ItemId { get; set; }
public int Quantity { get; set; }
}
public class CartReseted { }
Using Event Sourcing, the Write model consists of a set of events which are stored in the commonly named Event Store.
The EventStore
class should be redefine for your purpose. By default, the behavior of the EventStore
is to store events in-memory.
public abstract class EventStore<TEvent>
where TEvent : SimpleEvent
{
protected EventStore(IObservable<IEnumerable<TEvent>> eventAggregates) { }
public void Push(IEnumerable<TEvent> events);
public IObservable<TEvent> ObserveEvent();
public IObservable<TEvent> ObserveEvent<TEventType>();
}
If you need to make a persistent EventStore
, we offer a method called Persist
you can use to store events in a database system.
Using Event Sourcing, the Read model consists of reading a set of events from the Event Store to get a consistent view of the business.
Using this library, you will have the freedom to choose between two kind of views:
public abstract class InMemoryEventView<TEvent, TState>
where TEvent : SimpleEvent
where TState : class, new()
{
public TState State { get; }
protected InMemoryEventView(IObservable<TEvent> events, TState initialState = null) { }
public IObservable<TState> ObserveState();
public IObservable<TPartial> ObserveState<TPartial>(Func<TState, TPartial> selector);
}
In this particular form of EventView
, from an event you get a new state immediately available for consumption/subscription. This form of EventView
is close to the Redux pattern.
This other form of EventView
gives you the ability to override the Handle
method in which you can update a specific part of your data like a Table in a relational database.
public abstract class EventView<TEvent>
where TEvent : SimpleEvent
{
protected EventView(IObservable<TEvent> events) { }
public virtual void Replay(TEvent @event);
public virtual void Replay(IEnumerable<TEvent> events);
}
Notice that we offer a Replay
mechanism you can use to replay the history in case you changed the logic of the handler or the structure of the database.