One of the immensely awesome things that Udi Dahan has – if not invented, then at least brought to my attention – is the awesome “domain events – salvation” pattern.
If you’re familiar with the onion architecture, you’ve probably more than once experienced the feeling that you had the need to pull something from your IoC container some place deep in your domain model. In my experience, that often happens when you have a requirement on the form “when something happens bla bla, then some other thing must happen bla bla” where “something” and “some other thing” aren’t otherwise related.
An example could be when a new account (a.k.a. a customer that we mean to do business with) is registered in the system, we should make sure that our credit assessment of the account is fresh enough that we dare do the businesss.
I’ve found that the “domain events – salvation” provides a solution to that problem. Check this out:
1 2 3 4 5 6 7 8 9 |
public class Account { public void Register() { // ... do stuff that marks this account as registered DomainEvents.Raise(new AccountRegistered(this)); } } |
“Whoa, what was that?” you might ask… that was a domain object that did some stuff, and in the end it raised a “domain event”, telling to the world what just happened. And yes, that was a domain object calling out to some static nastiness – but I promise you, it isn’t as nasty as you may think.
I’d like to assure you that the Account class is fully unit testable – i.e. it can be tested in isolation, just like we want. And that’s because we can abstract the actual handling of the domain events out behind an interface, IHandleDomainEvents, which is what actually gets to take care of the domain events – something like this:
1 2 3 4 5 6 7 8 9 |
public class DomainEvents { public static IHandleDomainEvents Current = new DevNullDomainEventsHandler(); public static void Raise<T>(T domainEvent) { Current.Handle(domainEvent); } } |
and then IHandleDomainEvents can be implemented in several flavors and “injected” into the domain as a static dependency, e.g. this bad boy for testing:
1 2 3 4 5 6 7 8 9 10 11 |
public class TestDomainEventsHandlers : IHandleDomainEvents { readonly List<object> raisedDomainEvents = new List<object>(); public IEnumerable<object> RaisedDomainEvents { get { return raisedDomainEvents; } } public void Handle<T>(T domainEvent) { raisedDomainEvents.Add(domainEvent); } } |
which can be used by installing a new instance of it in the setup phase of each test, and then running assertions against the collected domain events in the assert phase.
The coolest part though, is the production implementation of IHandleDomainEvents – it may look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class CastleWindsorDomainEventDispatcher : IHandleDomainEvents { readonly IWindsorContainer container; public CastleWindsorDomainEventDispatcher(IWindsorContainer container) { this.container = container; } public void Handle<T>(T domainEvent) { var subscribers = container.ResolveAll<ISubscribeTo<T>>().ToList(); try { subscribers.ForEach(s => s.Handle(domainEvent)); } finally { subscribers.ForEach(s => container.Release(s)); } } } |
thus delegating the handling of domain events to all implementors of ISubscribeTo<TDomainEvent>. This way, if I have registered this guy in my container:
1 2 3 4 5 6 7 8 9 10 11 12 |
public class EnsureCreditAssessmentIsFresh : ISubscribeTo<AccountRegistered> { public EnsureCreditAssessmentIsFresh(ILookMaADependency lookMaADependency) { this.lookMaADependency = lookMaADependency; } public void Handle(AccountRegistered accountRegistered) { // possibly initiate new credit assessment in here... } } |
I can kick off a new credit assessment workflow when a new account is registered, and everything is wired together in a way that is semantically true to the domain. Also, my domain object suddenly get to pull stuff from the container (albeit in an indirect fashion), without even knowing about it!