-
Notifications
You must be signed in to change notification settings - Fork 1
2. Eventstore
The IEventstore
is your persistance layer that offers saving DomainEvents and restoring Entities by EventSourcing. There is a SnapShot function for performance reasons, if you need it. Inject the IEventStore
into your desired class and use the functions to save and load entities. The Eventstore uses optimistic concurrency so you have to give him the version of the entity that you are trying to save.
Storing and loading in the IEventstore
looks like this:
using Microwave.EventStores;
namespace Application.Users
{
public class UserCommandHandler
{
private readonly IEventStore _eventStore;
public SeasonCreatedEventHandler(IEventStore eventStore)
{
_eventStore = eventStore;
}
public async Task CreateUser(string userName)
{
UserCreatedEvent userCreatedEvent = User.Create(userName);
var result = await _eventStore.AppendAsync(userCreatedEvent, 0);
result.Check();
}
public async Task<User> LoadUser(GuidIdentity userId)
{
UserCreatedEvent userCreatedEvent = User.Create(userName);
var result = await _eventStore.LoadAsync<User>(userId);
result.Entity;
}
}
}
To append DomainEvents to the IEventStore
you have to implement the IDomainEvent
interface on your DomainEvents.
The interface forces you to implement the property EntityId
so the eventstore can assign the events to the entity.
Everything else is up to your choice. The EventStore
also generates upcounting versions for the DomainEvents and
the GlobalVersion that indicates where the event is placed in the overall EventStore. I would recommend to just
forward the EntityId like this, as this bears the least problems with persistance.
An immutable implementation could be:
public class UserCreatedEvent : IDomainEvent
{
public UserCreatedEvent(
Guid userId,
string name)
{
Name = name;
}
public Guid UserId { get; }
public Identity EntityId => UserId.ToString();
public string Name { get; }
}
To Load an entity the Entity has to implement the Interface IApply
wich takes a list of DomainEvents and forces you to apply them to your entity. There is a class Entity
that implements the IApply
method in a way, so the entity applies the DomainEvent to the Method that has to be implemented with IApply<T>
. Reflection is used so you might want to do it on your own, if you run into performance issues. Example:
public class User : Entity, IApply<UserCreatedEvent>, IApply<UserChangedNameEvent>
{
public void Apply(UserCreatedEvent domainEvent)
{
Id = domainEvent.UserId;
}
private void Apply(UserChangedNameEvent domainEvent)
{
Name = domainEvent.Name;
}
public Guid Id { get; private set; }
public string Name { get; private set; }
}
// OR with IApply
public class User : IApply
{
public void Apply(IEnumerable<IDomainEvent> domainEvents) // this here is basically what is done in the Entity class with reflection
{
foreach (var domainEvent in domainEvents)
{
switch (domainEvent)
{
case UserCreatedEvent ev: Apply(ev);
case UserChangedNameEvent ev: Apply(ev);
}
}
}
public void Apply(UserCreatedEvent domainEvent)
{
Id = domainEvent.UserId;
}
public void Apply(UserChangedNameEvent domainEvent)
{
Name = domainEvent.Name;
}
public Guid Id { get; private set; }
public string Name { get; private set; }
}
The IEventstore
supports snapshots that lets you save the state of an entity after a certain times of loading it,
so the eventstore does not need to apply too much events at once. The eventstore first loads the snapshot and then
applies the remaining events on it. The snapshots only get created when the entity is being loaded, so if you never
load the entity, the snapshot is not created on the given threshold. You can define this with the SnapShot<T>
class.
The <T>
must be of type IApply
and the parameter is the amount of events that have to be inserted before the IEventStore
saves a snapshot. To setup an Entity for Snapshots, add the
snapshopt configs at the startup class like this:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMicrowave(config =>
{
config.SnapShots.Add(new SnapShot<User>(10));
});
...
}