Skip to content

Instantly share code, notes, and snippets.

@yreynhout
Created May 5, 2015 16:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yreynhout/68e8fb6d119c6d5ec59a to your computer and use it in GitHub Desktop.
Save yreynhout/68e8fb6d119c6d5ec59a to your computer and use it in GitHub Desktop.
AggregateSource in a gist
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace AggregateSourceInGist
{
public interface IEventRouter
{
void Route(object @event);
}
public interface IConfigureEventRouter : IEventRouter
{
void Register<T>(Action<T> handler);
void Register(Type type, Action<object> handler);
}
public class EventRouter : IConfigureEventRouter
{
private readonly Dictionary<Type, Action<object>> _handlers;
public EventRouter()
{
_handlers = new Dictionary<Type, Action<object>>();
}
public void Register<T>(Action<T> handler)
{
if (handler == null)
throw new ArgumentNullException("handler");
_handlers.Add(typeof(T), _ => handler((T)_));
}
public void Register(Type type, Action<object> handler)
{
if (type == null)
throw new ArgumentNullException("type");
if (handler == null)
throw new ArgumentNullException("handler");
_handlers.Add(type, handler);
}
public void Route(object @event)
{
if (@event == null)
throw new ArgumentNullException("event");
Action<object> handler;
if (_handlers.TryGetValue(@event.GetType(), out handler))
{
handler(@event);
}
}
public static IConfigureEventRouter For(object instance)
{
if (instance == null)
throw new ArgumentNullException("instance");
var applyMethods = instance
.GetType()
.GetRuntimeMethods()
.Where(method => !method.IsStatic
&& method.Name == "Apply"
&& method.GetParameters().Length == 1
&& method.ReturnType == typeof(void))
.Select(method =>
{
var parameter = Expression.Parameter(typeof(object), "@event");
return new Tuple<Type, Action<object>>(
method.GetParameters().Single().ParameterType,
Expression.Lambda<Action<object>>(
Expression.Call(
Expression.Constant(instance),
method,
Expression.Convert(parameter, method.GetParameters().Single().ParameterType)),
parameter).
Compile());
});
var router = new EventRouter();
foreach (var applyMethod in applyMethods)
{
router.Register(applyMethod.Item1, applyMethod.Item2);
}
return router;
}
}
public interface IEventRecorder
{
IReadOnlyCollection<object> Events { get; }
void Record(object @event);
void Reset();
}
public class EventRecorder : IEventRecorder
{
private readonly List<object> _events;
public EventRecorder()
{
_events = new List<object>();
}
public IReadOnlyCollection<object> Events
{
get { return _events.ToArray(); }
}
public void Record(object @event)
{
_events.Add(@event);
}
public void Reset()
{
_events.Clear();
}
}
public interface IAggregateRootEntity
{
IEventRouter Router { get; }
IEventRecorder Recorder { get; }
}
public abstract class AggregateRootEntity : IAggregateRootEntity
{
private readonly IEventRouter _router;
private readonly IEventRecorder _recorder;
protected AggregateRootEntity()
{
_router = EventRouter.For(this);
_recorder = new EventRecorder();
}
protected void ApplyChange(object @event)
{
Play(@event);
Record(@event);
}
private void Record(object @event)
{
_recorder.Record(@event);
}
private void Play(object @event)
{
_router.Route(@event);
}
IEventRouter IAggregateRootEntity.Router
{
get { return _router; }
}
IEventRecorder IAggregateRootEntity.Recorder
{
get { return _recorder; }
}
}
public abstract class Entity : IEventRouter
{
private readonly Action<object> _applier; //the root's ApplyChange
private readonly IEventRouter _router;
protected Entity(Action<object> applier)
{
if (applier == null) throw new ArgumentNullException("applier");
_applier = applier;
_router = EventRouter.For(this);
}
public void Route(object @event)
{
_router.Route(@event);
}
protected void ApplyChange(object @event)
{
_applier(@event);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment