Domain Events – Don’t observe us, we’ll observe you
11. April 2010 von manuelk | 4 Comments
A few days ago I listened to a presentation by Eric Evans about the lessons learned since the publication of his hugely successful book “Domain Driven Design” in 2004.
Domain-driven design (DDD) is an approach to developing software for complex needs by deeply connecting the implementation to an evolving model of the core business concepts.
A thorough presentation of the principles, tenets and practices of DDD are outside the scope of this article. Instead, I would like to focus on one specific concept that Evans forgot to include when he wrote the book five years ago and which he today presents as one of the fundamental building blocks for successful DDD: Domain Events.
Captures the memory of something interesting which affects the domain
- Publisher / Subscriber instead of RPC
Instead of directly invoking all the consequences of a change in the system, an event is raised which can be handled by whomever is interested in it. - Event Sourcing instead of mutable state
Instead of altering the state of the system directly as soon as something happens, the change is reflected in the history (or log) of events. You can think of it as an incremental backup: Only the most recent changes must be saved because we rely on an existing record of all previous changes. This is known as Event Sourcing and I do not want to go into it further.
Now, event-driven programming is hardly new – most UI Frameworks s.a. ASP.Net and Winforms are based on exposing events on controls. The crux of the Domain-Event pattern is that not only do you use events in UI scenarios but in your core business model. An example should shed light on the differences in approach:
Given an Ecommerce application, the following functional requirements are given:
When a user wires money into his account:
- his credit card must be validated and charged
- his balance must be updated
- his status must be updated (maybe he became a preferred customer)
- he needs to be notified via email
- the event needs to be logged
In a traditional programming model, we would end up writing something like this:
DepositMoney(User u, double amount, currency curr)
{
ccInfo cc = Repository.GetCcInfo(u.UserID);
if( CreditCard.Authorize(cc, amount, curr) )
{
CreditCard.Settle(cc, amount, curr);
Payment.Add(u.UserID, amount, curr);
Notification.SendPaymentConfirmationMsg(u.UserID, amount, curr);
Log.PlayerPayment(u.UserID, amount, curr);
if( Account.IsEligibleForUpgrade(u.UserID) )
{
UpgradeInfo upg = Account.Upgrade(u.UserID);
Notification.SendCustomerUpgradeMsg(upg);
Log.CustomerUpgrade(u.UserID, upg);
}
}
else
{
Notification.SendCcRefusedMsg(u, cc);
Log.CcRefusal(u.UserID, cc, amount, curr);
}
}
The above example is obviously incomplete but it shows how fast we can find ourselves in a dependency hell, i.e. within a single method we reference six different modules (Repository, CreditCard, Payment, Notification, Log, Account) which ideally should not have any knowledge of each other.
Also the above design raises all kinds of inconvenient dilemmas, for example: the Log is always called after we send a Notification. Maybe we should include logging in the Notification methods? This would definitely reduce the chances of forgetting to log an event, but it would also create a direct dependency between the two unrelated modules and thus negate the principles of maximizing cohesion and separation of concerns.
It seems that either way, we’ll end up with messy code which will be hard to maintain – the first step towards the dreaded Big Ball of Mud architecture.
How can Domain Events help?
With Domain Events the above method simply raises a DepositRequestedEvent, which includes the references to the user, amount and currency. Other modules subscribe to this type of event. In our case, a PaymentService subscribes to the DepositRequestedEvent, and upon observing such an event authorizes and settles the credit card, updates the balance and raises in turn a PaymentAddedEvent or a CcRefusedEvent. These events are handled independently by an AccountService, a NotificationService and an LogService. The AccountService may raise a CustomerUpgradedEvent which is handled by NotificationService and LogService. Whenever something important happens in the system another event is raised.
This way we can drastically reduce the amount of interdependencies because modules do not call each other directly. Since this is an asynchronous messaging model our system becomes more scalable and may even deliver better performance because tasks can be processed in parallel.
Obviously this kind of architecture demands a sophisticated subscriber/publisher infrastructure for managing the message streams and event subscriptions. In this article I want to briefly present two such frameworks:
- Udi Dahan’s NServiceBus
- .Net 4.0 Reactive Framework (RxFramework)
NServiceBus is a one-way asynchronous queued messaging platform which was developed with the following focus in mind:
-
Inherent Publisher / Subscriber support (each message sent can be viewed as an event raised)
-
Developer friendly (Painless installation and usage)
-
Reliability and Scalability (NServiceBus is built upon MSMQ which facilitates persisted and thus reliable messaging)
-
Support for long-running workflows (or “Sagas” as they are called in NServiceBus terminology)
-
Extensibility
One of the great features of NServiceBus is that events are defined as classes which implement one or more interfaces. A subscriber subscribes to an Event via its Interface. That means that different events can be handled by the same subscription as long as they implement a common interface (e.g. IEmailNotification or ILoggable).
As a consequence, the decision whether an event is handled or not depends neither on the subscriber (who handles all events which implement a certain interface) nor on the publisher (who only knows the type of the event, not all the implemented interfaces) but on the event itself. This adds a level of cohesion to all the components involved which is very difficult to achieve in the classic approach above. To quote Udi Dahan:
[Domain Events based architecture] complies with the Single Responsibility Principle, so the business requirement which states that when a customer becomes preferred, they should be sent an email belongs somewhere else.
Notice that the key word in the requirement – “when”.
Any time you see that word in relation to your domain, consider modeling it as a domain event.
For an excellent discussion of how NServiceBus can assist in developing a domain events based application, read Udi Dahan’s article. In case you want to listen to a one-hour talk of Udi about NServiceBus architecture, check out this podcast.
As opposed to NServiceBus the RxFramework does not provide a reliable messaging solution. Instead it attempts to change the way we as programmers deal with sequentially arriving input. Until now the dominating pattern regarding sequential input was the IEnumerable interface and the “yield” keyword in C#. Since the advent of LINQ and the deferred execution mechanism, this Design Pattern has received a lot of attention. The idea is very simple: Instead of looking at the complete sequence of inputs up front, we only retrieve one member at a time.
The RxFramework takes this idea one step further and postulates: Instead of “pulling” each member of the sequence by explicitly asking for it, let’s subscribe to the sequence and whenever the next member “appears”, we will be notified. In this model, a stream of events is treated as data which can be filtered and processed very similarly (in fact identically) to the way we are used to processing any list or sequence of regular objects. Wes Dyer has written an excellent introduction to the RXFramework, which clarifies this point and shows how it makes .Net Events more manageable.
We can utilize this ability to implement the event-domain pattern. In fact, Mike Chaliy presents a nice example of how to implement a money transfer scenario (very similar to the one above) as domain events via the RxFramework.
Conclusion
Domain Events are a recognized architectural pattern which is gaining more and more popularity among the designers of highly scalable and robust systems. The reasons for its rising popularity are threefold:
- As Applications become more complex, the need for better code organization increases
- The need for highly scalable applications grows steadily
- The availability of supporting Frameworks enable developers to implement a Domain Events architecture without having to develop their own messaging solutions
Two frameworks were presented which facilitate two radically different approaches to Domain Event based architecture:
- NServiceBus: a platform for reliable, asynchronous, one-way messaging and long running workflows
- RxFramework: treats a stream of events like data which can be filtered and manipulated easily
Manuel Korn
1. Tal
Comment of 22. April 2010 at 14:39
Brilliant post!
2. Noam Klier
Comment of 4. May 2010 at 16:45
Great post! I can think of many places where I could (and should) have used this sort of architecture
3. tuhnurnscency
Comment of 7. June 2010 at 02:17
Just want to say what a great blog you got here!
I’ve been around for quite a lot of time, but finally decided to show my appreciation of your work!
Thumbs up, and keep it going!
Cheers
Christian,Earn Free Vouchers / Cash
4. electronic cig
Comment of 7. June 2010 at 09:14
One again, your idea is very
good.thank you!very much.