Event sourcing support for MSR Orleans (http://orleans.codeplex.com) using EventStore (http://geteventstore.com)
Compile the solution first. This will copy the assemblies to the local silo folder (post-build event).
Start EventStore.
Configure EventStoreProvider in DevTestServerConfiguration.xml
. Connection string is in <hostname>:<port>
format.
<OrleansConfiguration xmlns="urn:orleans">
<Globals>
<StorageProviders>
<Provider Type="Orleans.EventSourcing.EventStoreStorage.EventStoreProvider" Name="EventStore" ConnectionString="localhost:1113" Username="admin" Password="changeit" />
Username and Password can be omitted if you're using default values.
Run the Test.Client project. This will start the local silo and execute sample code from Program.Main().
The most important thing to understand is that it is the state that is event sourced, not the grain.
The second most important thing is that event sourcing is hidden in the Implementation project and not present in the Interfaces project - the fact that a certain grain is event sourced is encapsulated in this grain and doesn't impact other grains.
Define your state
Derive from IAggregateState
instead of IState
.
public interface IPersonState : IAggregateState
{
string FirstName { get; set; }
string LastName { get; set; }
GenderType Gender { get; set; }
bool IsMarried { get; set; }
}
Define your events
Events are plain classes. By convention, add a public void Apply(IYourAggregateState state)
method that will mutate the state by applying the current event.
public class PersonRegistered
{
public string FirstName { get; set; }
public string LastName { get; set; }
public GenderType Gender { get; set; }
public void Apply(IPersonState state)
{
state.FirstName = this.FirstName;
state.LastName = this.LastName;
state.Gender = this.Gender;
}
}
Raise events from the grain
Call RaiseEvent
method passing your event in the grain. RaiseEvent
will mutate the grain state so you don't have to do it yourself. By default the event will be persisted in event store. If you know that you will be raising multiple events and want to persist them in a single commit, you can pass store: false
argument. In that case the state will be mutated but the event won't be persisted until RaiseEvent(@event, store: true)
or this.State.WriteStateAsync()
is called.
Task IPerson.Register(PersonalAttributes props)
{
return this.RaiseEvent(new PersonRegistered
{
FirstName = props.FirstName,
LastName = props.LastName,
Gender = props.Gender
});
}
Currently the project supports EventStore only and the code may not be performant enough for a high traffic production environments (using dynamic dispatch through dynamic
objects).
Planned work includes supporting other event stores, by either implementing own provider model or using Common Domain (https://github.com/NEventStore/CommonDomain)