Created
May 5, 2015 16:59
-
-
Save yreynhout/68e8fb6d119c6d5ec59a to your computer and use it in GitHub Desktop.
AggregateSource in a gist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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