Event Sourcing and CQRS, Dispatch options.
By Jérémie Chassaing on Tuesday, November 3, 2009, 15:41 - Domain Driven Design - Permalink
As seen in previous post, I used dynamic to replay events.
The main reason to use it was to avoid long code using reflection in the infrastructure that would have made it hard to read.
I’ll show several ways to do this dispatch with pros and cons in each cases.
Dynamic
The proposed solution was using dynamic.
+ Pros : there is no reflection code involved, code is very simple.
- Cons : all state change (Apply) methods must have the same name.
I made no performance test, so I cannot judge if perf is better or not. It seems that the DLR has a rather good cache when the same type is encountered several time, but only measures can tell.
Handlers registration
This is the current implementation in Mark Nijhof’s sample.
The base class maintains a dictionary of Type/Action<T> association to dispatch events based on type.
Since an Action<T> delegate must have a target instance, the delegate must be constructed from within the instance, in the .ctor.
public class AggregateRoot<TId>
{
readonly Dictionary<Type, Action<object>> handlers =
new Dictionary<Type, Action<object>>();
protected void Register<T>(Action<T> handler)
{
handlers.Add(typeof(T),e => handler((T)e));
}
protected void Replay(IEnumerable<object> events)
{
foreach (var @event in events)
handlers[@event.GetType()](@event);
}
// rest of the aggregate root class
}
Here is code that use it :
public class Book : AggregateRoot<BookId>
{
private readonly BookId id;
public Book(BookId id,IEnumerable<object> events) : this(id)
{
Replay(events);
}
public Book(BookId id,string title, string isbn) : this(id)
{
var @event = new BookRegistered(id, title, isbn);
OnBookRegistered(@event);
Append(@event);
}
private Book(BookId id)
{
this.id = id;
Register<BookRegistered>(OnBookRegistered);
Register<BookLent>(OnBookLent);
Register<BookReturned>(OnBookReturned);
}
private void OnBookRegistered(BookRegistered @event) { /**/ }
private void OnBookLent(BookLent @event) { /**/ }
private void OnBookReturned(BookReturned @event) { /**/ }
}
+Pros : Still no reflection,
Meaningful
method names
-Cons : Additional plumbing code,
Private
constructor to avoid repetition
Registration
occurs at each instantiation
Convention Based Method Naming
This is the way advocated by Greg Young.
If your event is called BookRegistered, assume the method will be called OnBookRegistered, and find it by reflection. You can implement a cache at class level to avoid reflection on each dispatch.
public abstract class AggregateRoot<TId> : IAggregateRoot<TId>
{
private static readonly Dictionary<Type, IEventDispatcher> Handlers =
new Dictionary<Type, IEventDispatcher>();
private static readonly object HandlersLock = new object();
protected void Replay(IEnumerable<object> events)
{
var dispatcher = GetDispatcher();
dispatcher.Dispatch(this, @events);
}
private IEventDispatcher GetDispatcher()
{
IEventDispatcher handlers;
var type = GetType();
lock (HandlersLock)
{
if (!Handlers.TryGetValue(type, out handlers))
{
handlers = EventDispatcher.Create(type);
Handlers.Add(type, handlers);
}
}
return handlers;
}
... rest of the code here
}
The dispatcher code :
internal interface IEventDispatcher
{
void Dispatch(object target, IEnumerable<object>events);
}
internal class EventDispatcher<T> : IEventDispatcher
{
private readonly Dictionary<Type, IEventHandler<T>> handlers;
public EventDispatcher()
{
var h = from m in typeof(T)
.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic)
let parameters = m.GetParameters()
where parameters.Length ==1
&& m.Name == "On" + parameters[0].ParameterType.Name
select EventHandler.Create<T>(m);
handlers = h.ToDictionary(i => i.EventType);
}
public void Dispatch(object target, IEnumerable<object> events)
{
var typedTarget = (T)target;
foreach (var @event in events)
{
var handler = handlers[@event.GetType()];
handler.Call(typedTarget, @event);
}
}
}
internal static class EventDispatcher
{
public static IEventDispatcher Create(Type type)
{
return (IEventDispatcher)Activator.CreateInstance(
typeof(EventDispatcher<>).MakeGenericType(type));
}
}
and the event handler :
internal interface IEventHandler<T>
{
void Call(T target, object argument);
Type EventType { get; }
}
internal class EventHandler<TEntity, TEvent> : IEventHandler<TEntity>
{
private readonly Action<TEntity, TEvent> handler;
public EventHandler(MethodInfo methodInfo)
{
handler = (Action<TEntity, TEvent>)Delegate.CreateDelegate(
typeof(Action<TEntity, TEvent>), methodInfo, true);
}
public void Call(TEntity target, object argument)
{
handler(target, (TEvent)argument);
}
public Type EventType
{
get { return typeof(TEvent); }
}
}
internal static class EventHandler
{
public static IEventHandler<T> Create<T>(MethodInfo methodInfo)
{
var eventType = methodInfo.GetParameters()[0].ParameterType;
return (IEventHandler<T>)Activator.CreateInstance(
typeof(EventHandler<,>)
.MakeGenericType(typeof(T), eventType),
methodInfo
);
}
}
The trick here is to create a static delegate with two parameters from an instance method info that take one parameter (and one implicit this target).
This way, the delegate is not tied to a specific instance and can be used on any target.
As you can see, this option requires more code ! I did not want to start with that.
+Pros : Convention base names mean no manual mapping, mapping is
implicit
Binding is
made a class level instead of instance level
-Cons : Only unit tests can tell when you mess with names
Not immune
to event name change, should have good unit tests !
Apply then Append
I also had a remark that if I forget Append after Apply, I’ll get in trouble.
In Handler Registration option and Convention base method naming, the dispatch can be done by the base class, so I could tell the base class to dispatch then Append then event to UncommittedEvents.
This way you end with something like :
var @event = new BookLent(/**/);
Play(@event);
where play dispatches the event to the right method and appends.
This way you cannot forget.
My problem with this, especially in the Convention base method naming scenario is that nobody references the event application methods anymore. Resharper will report them as unused methods, and you won’t know unless you run unit tests.
Moreover, you pay the cost of a dynamic dispatch when you know your event type.
Perhaps something like this could be better :
var @event = new BookLent(/**/);
Play(@event).With(OnBookLent);
the implementation is not very complicated :
public class AggregateRoot<TId>
{
private readonly UncommittedEvents uncommittedEents;
protected EventPlayer<TEvent> Play<TEvent>(TEvent @event)
{
return new EventPlayer<TEvent>(@event, uncommitedEvents);
}
... rest of the code here
}
public struct EventPlayer<TEvent>
{
private readonly TEvent @event;
private readonly UncommittedEvents uncommittedEvents;
internal EventPlayer(TEvent @event, UncommittedEvents uncommittedEvents)
{
this.@event = @event;
this.uncommittedEvents = uncommittedEvents;
}
public void With(Action<TEvent> handler)
{
handler(@event);
uncommittedEvents.Append(@event);
}
}
This way, methods are referenced at least once with type check.
My mind is still not set… What do you prefer ?
Comments
In Mark's example you can make the dispatches static as well instead of doing them on every instance just make the handlers of the form
private static void ApplyFoo(MyType object, FooEvent event) {}
@greg> yes, it is possible, but it becomes a bit ugly to use static methods for this.
With reflection you can emit a static delegate (AggregateType, EventType) from an instance delegate(EventType) ... that's all in the hidden plumbing and does not appear in your domain object's code.
inside ApplyFoo(MyAggregateRoot ar, MyEvent @event) you would have things like :
ar.borrower = @event.Borrower;
etc.
it does not sound like applying state change internally anymore.
The Resharper greying can be annoying at times but conventions are specifically intentioned that everyone must understand them before coding. So as long as everyone agrees and understands the convention and it is simple to do so, it really is not a problem.
Personally, I use both approaches. I register default apply actions based upon a method naming convention, but the convention can also be overridden with a manually registered action. Additionally, if you are really annoyed about the "unused" method names, you could have an Applies<> interface that the AR explicitly implements for each event.
@Gilligan> You're true, you can add attribute or any other mean to bypass convention, I let it as an exercise for readers.
I don't quite like the Applie<T> interface because it makes private methods public, event if implemented explicitly...
We're currently doing static registrations like this in the static object constructor:
Register<UserCreatedEvent>((user, message) => user.OnCreated(message));
When a message is received, the base aggregate class does a lookup on the message type from the handler dictionary:
handlers.TryGetValue(message.GetType, out handler)
And if we get a handler, we invoke it like this:
handler(this as TDerivedAggregate, message)
Unit testing then uncovers if we've forgotten to register a handler.
This topic is a little bit tricky because you could go either direction. In general, I don't like frameworks that magically call methods with special names (remember Page_Init and Application_Start?).
@jonathan> It's an interesting mix between the static dispatch and handler registration. Here you cannot avoid the lambda, method group won't do the job since you're in a static context.
Here's a full sample with irrelevant details removed:
http://gist.github.com/225264
Like you, I want to promote the domain and eliminate infrastructure-related concerns. I'm still thinking about the naming of the "Apply" method. The other thing that I'm not too thrilled about is the generic type parameter that is now required on the base aggregate class as a result of static handler registrations (because we must cast to "as TAggregate" in the Apply method). This appears to be the price we pay in a statically typed environment.
The difficult or "messy" code is hidden inside the MessageDispatcher class, which keeps the derived and base aggregate classes relatively simple.
In any case, it's C# 3.5 compliant which won't be a concern for too much longer. I've always been highly skeptical of auto-wireups, but that was in frameworks that were completely untestable, e.g. ASP.NET WebForms. In this situation, since the domain can be under test, it may turn out to be a non-issue.
@jonathan> Like you I'm not comfortable with class MyClass : BaseClass<MyClass>... you cannot enforce that the generic parameter is the actual child class.
But if registering handlers, I prefer in the static .ctor like this that in instance .ctor.
One other possibility to get rid of the "unused code" warnings in both FxCop and Resharper would be to declare each of your OnWhateverEvent methods protected and possibly virtual?. Then, just don't inherit from that class.
Have you found any need for inheritance with your domain aggregates/entities?
@jonathan> But private virtual methods are a bit ugly too... And I see no reason to make those methods protected or public.
The two main point here are :
Perhaps we should prepare the addition of the event to uncommited events at event creation, and actually add it after.. with a construct like using ?
One thought that I had related to Greg's reflection-based scenario: Is it a requirement to have the "On" prefix for the event handling method? Why not just search all methods that take a single parameter of type IEvent?
I guess the problem would be if the user starts passing a particular event around to more than one method, then we wouldn't know which method to call.
@jonathan> it's a possible option. You could simply disambiguate methods using attributes if needed, but it would be auto wired by default.
Hi Jeremie,
We started with handler registration, but it is surely memory and time consuming to register on each aggregate. Then, we face the same challenges:
- I do not like much adding a parameter to each Apply method and make them static
- considering dynamic and convention based, I do not like the fact that R# says my methods are unused (like too much remove code with this, I do not want to have to think about that, even if I agree with Gilligan when he says we have to understand before coding...) and I do not want to change my method to protected or public...
- as Jonathan and you, I do not like the generic type parameter that is now required on the base aggregate class if you consider Jonathan's approach
I see an other solution :
- RegisterEvent is an instance method, that is called in RegisterAllEvents override as in handler registration, but use a static register inside Aggregate, it will remove redundancy of register (memory and time consuming)
- then, you have registered an "apply" method that apply to one particular aggregate instance (RegisterEvent<SomeEventHappen>(e => this.Apply(e)), so you have to remove the reference to "this" instance, here is the trick: you use same registration process as in convention based solution, but rather than using name convention, you use Expression API to retrieve method name on registered Expression e => this.Apply(e).
Note that you can enforce some convention on method signature. Also you cannot use RegisterEvent<SomeEventHappen>(Apply) since Apply is a delegate, not an expression.
Clément