Skip to content

Instantly share code, notes, and snippets.

@AlexeyRaga
Created November 25, 2012 02:18
Show Gist options
  • Save AlexeyRaga/4142133 to your computer and use it in GitHub Desktop.
Save AlexeyRaga/4142133 to your computer and use it in GitHub Desktop.
Projection prototype
//There is also a set of (extension) methods
//such as Add, Update, etc.
//all these methods use AddOrUpdate inside.
public interface IDocumentWriter<in TKey, TDocument>
{
//The idea of AddOrUpdate is borrowed from ConcurrentDictionary:
//AddOrUpdate(TKey key, Func<TKey, TView> createNewToAdd, Func<TView, TView> updateExisting);
TDocument AddOrUpdate(TKey key, Func<TDocument> addFactory, Func<TDocument, TDocument> updateFactory);
bool TryDelete(TKey key);
bool Exists(TKey key);
}
//The idea of a projection is to accumulate and maintain some persisted state
//based on events
public sealed class RegistrationStatusProjection
{
//just a writer who knows how to persist a projection
//The first type parameter is a key, the second is the PVM type.
//Look at IDocumentWriter interface in this gist
private IDocumentWriter<Guid, RegistrationStatusView> _writer;
public RegistrationStatusProjection(IDocumentStore factory)
{
//create a writer.
//in my case it is a file system writer that will create a file like:
//{Guid}-RegistrationStatusView.pvm
//where the content is just a RegistrationStatusView serialized into JSON.
//Obviously it can be anything: MongoDB persister, SQL 1NF, RavenDB, SOLR, OLAP, etc.
_writer = factory.GetWriter<Guid, RegistrationStatusView>();
}
//In my case I use a simple "When(IEvent)" convention which allows me to keep
//my projections clean and simple.
//Another way could be to implement a bunch of IHandle<TEvent> interfaces,
//but I really prefer conventions when possible.
public void When(RegistrationStarted evt)
{
//my writer interface has a set of (extension) methods
//such as Add, Update, etc.
_writer.Add(evt.RegistrationId, new RegistrationStatusView
{
Status = RegistrationProcessStatus.InProgress,
Login = evt.Security.Login,
DisplayName = evt.Profile.DisplayName,
Email = evt.Security.Email,
Errors = new List<string>()
});
}
public void When(RegistrationFailed evt)
{
//here I am updating the existing registration projection
//AddOrUpdate will load one for me and put it as a "view" parameter
//into my anonymous function.
//There I just change some properties and the view will be saved back.
_writer.AddOrUpdate(evt.RegistrationId, view =>
{
view.Status = RegistrationProcessStatus.Failed;
view.Errors = evt.Errors.ToList();
});
}
public void When(RegistrationCompleted evt)
{
_writer.AddOrUpdate(evt.RegistrationId, view =>
{
view.Errors = new List<string>();
view.Status = RegistrationProcessStatus.Successful;
});
}
//Damn simple PVM
[Serializable]
public sealed class RegistrationStatusView
{
public string Login { get; set; }
public string Email { get; set; }
public string DisplayName { get; set; }
public List<string> Errors { get; set; }
public RegistrationProcessStatus Status { get; set; }
}
public enum RegistrationProcessStatus
{
InProgress = 0,
Successful = 1,
Failed = 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment