Almost always, when writing even the simplest of systems, the need arises for some kind of event publishing/subscription mechanism. There are of course many ways to achieve this, but I have found that Castle Windsor can provide everything that I need to build a simple, yet incredibly powerful event bus.
Consider this example from the real world: mortgage deeds follow many paths through our mortgage deed administration system, and one of them is purchase for the administrator’s own portfolio. Upon recording the purchase, it is crucial that the debtors are enlisted in our subscription at the Central Office Of Civil Registration (CPR in Danish), allowing us to receive updates whenever people change names or move without telling us.
A naive implementation of the
Record method on the
Purchase could look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
public class Purchase : Transaction { // ... public virtual void Record() { // ... // // and then: foreach(var debtor in MortgageDeed.CurrentDebtors) { Enlist(debtor.LegalEntity); } } void Enlist(LegalEntity entity) { ServiceLocator.Resolve<ICprEnlistment>().Enlist(entity); } } |
– but this is ugly! Enlisting the debtors has no direct relation to the logic of how to record a purchase, and when this requirement came, this implementation caused numerous tests to break. You could of course argue that our tests should not rely on the record method, but that was however the case, and it was a real P.I.T.A. to implement this.
Moreover, this approach forces us to grab our service locator inside a domain entity, which is really bad for too many reasons to mention here.
What we SHOULD have done, and what I have been doing ever since, is to create a simple event bus that resolves handlers to messages through Windsor (actually directly through the kernel because it is automatically injected). This way, I could have implemented the record method like this:
|
public class Purchase : Transaction { // ... public virtual void Record() { // ... // // and then: EventBus.Publish(new PurchaseRecorded(Id)); } } |
– and then this method would never change again (at least not for the wrong reasons)…
Then I have my simple event bus mediator (just an ounce of indirection, allowing my entities to be unaware of the service locator):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
public static class EventBus { [ThreadStatic] static IBus bus; public static void Publish(object message) { GetBus().Publish(message); } static IBus GetBus() { return bus ?? (bus = CreateBus()); } static IBus CreateBus() { return EventBusConfiguration.TestMode ? new StubBus() : ServiceLocator.Resolve<IBus>(); } } public static class EventBusConfiguration { public static bool TestMode { set; get; } } |
– which, depending on the bool inside the static
EventBusConfiguration class, will be either a fake bus that swallows all its messages, or the real deal:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
public class EventBus : IEventBus { IKernel kernel; public EventBus(IKernel kernel) { this.kernel = kernel; } public void Publish(object message) { // get type of published message var messageType = message.GetType(); // close the handler type to be subscribers // of this exact type of message var handlerType = typeof(ISubscribesTo<>) .MakeGenericType(messageType); // make MicroKernel do its thing var handlers = kernel.ResolveAll(handlerType); // invoke handlers foreach(object handler in handlers) { var method = handler.GetType().GetMethod("Handle"); method.Invoke(object, new [] {message}); kernel.ReleaseComponent(handler); } } } |
This means that subscribing to a message is as simple as registering implementations of this:
|
public interface ISubscribesTo<T> { void Handle(T message); } |
in my Windsor Container, e.g. the handler that enlists people in our subscription at the Central Office Of Civil Registration:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
public class EnlistLegalEntitiesWithCpr : ISubscribesTo<PurchaseRecorded> { ICprEnlistment cprEnlistment; ITransactionRepository transactionRepository; public EnlistLegalEntitiesWithCpr(ICprEnlistment cprEnlistment, ITransactionRepository transactionRepository) { this.cprEnlistment = cprEnlistment; this.transactionRepository = transactionRepository; } public void Handle(PurchaseRecorded message) { var purchase = transactionRepository.FindById(message.Id); var debtors = purchase.MortgageDeed.CurrentDebtors; foreach(var debtor in debtors) { cprEnlistment.Enlist(entity); } } } |
This is really cool, because it allows one to build systems where logic is deliciously decoupled, and you can add stuff, and then some more stuff, and the new stuff will not break the old stuff, because it doesn’t interfere with it.
Moreover, if you feel like extending it a little bit, it shouldn’t take that much of an effort to publish the events into a message queue for other systems to handle. If, for example, it took a while to process something, then I would definitely benefit from letting another process take care of that while my web request continues. Or if I wanted to update my data warehouse, I would build an aggregator in another process that did batch updates of the warehouse.
Oh, and this is one of the reasons that I would not hesitate to choose Castle Windsor if I were allowed to bring only one thing to a deserted island… you can build almost anything with a cool IoC container.