Update: The next post in this series is now online here.
My previous post on how to create fully encapsulated domain models introduced the concept of events as a core pattern of communication from the domain back to the service layer. In that post, I put up enough code to get the idea across but didn’t address issues like memory leaks and multi-threading. This post will show the solution to those two critical points.

I’ve snipped out one of the events in the previous example for brevity.
Previous API
The previous API looked like this:
1: public static class DomainEvents
2: {
3: public static event EventHandler GameReportedLost;
4: public static void RaiseGameReportedLostEvent()
5: {
6: if (GameReportedLost != null)
7: GameReportedLost(null, null);
8: }
9:
10: public static event EventHandler CartIsFull;
11: public static void RaiseCartIsFull()
12: {
13: if (CartIsFull != null)
14: CartIsFull(null, null);
15: }
16: }
One thing that we want to keep in the solution is that all the code to define events, their names, and the parameters they bring will be in one place – in this case, the DomainEvents class. One thing that we’d like to fix is the amount of code needed to define an event.
Previous Service Layer
Here’s what our previous service layer code looked like:
1: public class AddGameToCartMessageHandler :
2: BaseMessageHandler<AddGameToCartMessage>
3: {
4: public override void Handle(AddGameToCartMessage m)
5: {
6: using (ISession session = SessionFactory.OpenSession())
7: using (ITransaction tx = session.BeginTransaction())
8: {
9: ICart cart = session.Get<ICart>(m.CartId);
10: IGame g = session.Get<IGame>(m.GameId);
11:
12: Domain.DomainEvents.GameReportedLost +=
13: gameReportedLost;
14: Domain.DomainEvents.CartIsFull +=
15: cartIsFull;
16:
17: cart.Add(g);
18:
19: Domain.DomainEvents.GameReportedLost -=
20: gameReportedLost;
21: Domain.DomainEvents.CartIsFull -=
22: cartIsFull;
23:
24: tx.Commit();
25: }
26: }
27:
28: private EventHandler gameReportedLost = delegate {
29: Bus.Return((int)ErrorCodes.GameReportedLost);
30: };
31:
32: private EventHandler cartIsFull = delegate {
33: Bus.Return((int)ErrorCodes.CartIsFull);
34: };
35: }
36: }
Another thing that should be improved is the amount of code needed in the service layer.
Raising an event, though, should still be fairly simple – one line of code similar to DomainEvents.RaiseGameReportedLost().
New API
Here’s what the new API looks like:
1: public static class DomainEvents
2: {
3: public static readonly DomainEvent<IGame> GameReportedLost =
4: new DomainEvent<IGame>;
5:
6: public static readonly DomainEvent<ICart> CartIsFull=
7: new DomainEvent<ICart>;
8: }
It looks like we’ve managed to bring down the complexity of defining an event.
Raising an event is slightly different, but still only one line of code (”this” refers to the Cart class that is calling this API): DomainEvents.CartIsFull.Raise(this);
New Service Layer
The advantage of having a disposable domain event allows us to use the “using” construct for cleanup.
1: public class AddGameToCartMessageHandler :
2: BaseMessageHandler<AddGameToCartMessage>
3: {
4: public override void Handle(AddGameToCartMessage m)
5: {
6: using (ISession session = SessionFactory.OpenSession())
7: using (ITransaction tx = session.BeginTransaction())
8: using (DomainEvents.GameReportedLost.Register(gameReportedLost))
9: using (DomainEvents.CartIsFull.Register(cartIsFull))
10: {
11: ICart cart = session.Get<ICart>(m.CartId);
12: IGame g = session.Get<IGame>(m.GameId);
13:
14: cart.Add(g);
15:
16: tx.Commit();
17: }
18: }
19:
20: private Action<IGame> gameReportedLost = delegate {
21: Bus.Return((int)ErrorCodes.GameReportedLost);
22: };
23:
24: private Action<ICart> cartIsFull = delegate {
25: Bus.Return((int)ErrorCodes.CartIsFull);
26: };
27: }
28: }
I also want to mention that you don’t necessarily have to have the same service layer object handle these events as that which calls the domain objects. In other words, we can have singleton objects handling these events for things like sending emails, notifying external systems, and auditing.
The Infrastructure
The infrastructure that makes all this possible (in a thread-safe way) is quite simple and made up of two parts, the DomainEvent that we saw being used above, and the DomainEventRegistrationRemover which handles the disposing:
1: using System;
2: using System.Collections.Generic;
3:
4: namespace DomainEventInfrastructure
5: {
6: public class DomainEvent<E>
7: {
8: [ThreadStatic]
9: private static List<Action<E>> _actions;
10:
11: protected List<Action<E>> actions
12: {
13: get {
14: if (_actions == null)
15: _actions = new List<Action<E>>();
16:
17: return _actions;
18: }
19: }
20:
21: public IDisposable Register(Action<E> callback)
22: {
23: actions.Add(callback);
24: return new DomainEventRegistrationRemover(delegate
25: {
26: actions.Remove(callback);
27: }
28: );
29: }
30:
31: public void Raise(E args)
32: {
33: foreach (Action<E> action in actions)
34: action.Invoke(args);
35: }
36: }
37: }
38:
Note that the invocation list of the domain event is thread static, meaning that each thread gets its own copy – even though they’re all working with the same instance of the domain event.
Here’s the DomainEventRegistrationRemover – even simpler:
1: using System;
2:
3: namespace DomainEventInfrastructure
4: {
5: public class DomainEventRegistrationRemover : IDisposable
6: {
7: private readonly Action CallOnDispose;
8:
9: public DomainEventRegistrationRemover(Action ToCall)
10: {
11: this.CallOnDispose = ToCall;
12: }
13:
14: public void Dispose()
15: {
16: this.CallOnDispose.DynamicInvoke();
17: }
18: }
19: }
For your convenience, I’ve made these available for download here.
I also want to add that if you haven’t looked at the comments on the original post – there’s some really good stuff there (36 comments so far). Take a look.