Domain Driven Design with Web API extensions part 3: starting with domain events
October 29, 2015 1 Comment
In the previous post we looked at how to apply the decorator pattern for our emailing scenario in the domain driven design demo project. We saw how the pattern helped us augment the functionality of the original TimetableService in an object oriented fashion. We also managed to wire up the decorator in StructureMap.
In this post we’ll solve the same problem in a different way. Well, at least we’ll start looking at another solution. Also, we’ll start discussing another concept from DDD: domain events. This part of the topic will be divided into two posts: the current post lays the theoretical foundations for the concepts and the next post will show the code.
Domain events are events that happen within a domain. Events are actions that occur within a domain that other components might be interested in. These events are a way for the domain to let the world know that something important has happened. Those other components are then free to do with that information what they want. In other words domain events are a means of communication for the domains in a decoupled and independent fashion. The domain won’t have any knowledge of the elements that are waiting for the message. Therefore we can avoid adding dependencies to the domain object – which in fact would be erroneous as we would unnecessarily bloat its scope.
One issue with our current emailing logic is that we only allow for one action to follow upon a load test insertion or update: send an email. If we’d like to allow other components to consume that information then we have to keep adding decorators or extending the constructor of Timetable service with new abstractions for the interested parties. It would be much better if the Timetable domain could somehow signal that “Hello everyone, I have just validated and inserted/updated a couple of load tests” whereby all other components can take part of that information and process it in some way. E.g. a logging or a caching mechanism may also be interested in the load test insertion event. You may need to write a logging and a caching decorator or extend the constructor of TimetableService with ILogger and ICache but we’ll see an alternative you can consider.
This is a case for the publisher-subscriber or producer-consumer pattern in software architecture. An element publishes messages and zero, one or more subscribers subscribe to them. The publisher has no knowledge of the subscribers at all, they are independent components. This setup is similar to a Twitter account. If you publish a message on Twitter then you have very little control over what happens to it after, your subscribers can do what they want with your tweet: read it, dismiss it, ignore it, retweet it, respond to it etc. Also, you normally cannot control directly who will follow you, i.e. who will subscribe to your messages. Your subscribers are equally free to unfollow you as they wish.
There may be both internal and external elements that are interested in domain events. By internal elements I mean classes within the same solution, like the emailing class we introduced in the previous 2 posts. External elements are applications that are independent of the one where the domain resides and where the domain publishes the events. E.g. there may be an accounting application that’s independent of the load testing project which also wants to know of new load tests in order to calculate the expected monthly profit for the company.
We’ll first consider internal communication with a so-called mediator in this and the next post. We’ll then turn our attention to external communication with the help of a messaging system. If that sounds intimidating then don’t worry. We’ll keep it simple and you’ll see that it doesn’t need to be complex at all. There are very reliable and tested messaging systems out there that you can safely use in your application.
A mediator is the most important element in the mediator pattern. This post shows you the usage of the mediator pattern in conjunction with delegates and events. You can go through the provided links to read more about in details, I won’t repeat everything here.
In short a mediator provides a means of communication between independent objects. It’s an object where publishers can send their messages and subscribers can subscribe to them. The example provided in the first link in the previous paragraph looks at case where similar objects communicate with each other indirectly. In our case we’ll be more interested in the example provided in the second link with events and delegates. Note that I didn’t manage to apply that same solution to the emailing scenario but we’ll approximate events, event arguments and delegates in a different way.
Before we see any code let’s see how the elements will be connected:
- The mediator class is located in the shared kernel where all other layers can access it
- The mediator has methods for registering the subscribers and publishing the events
- The subscribers can register themselves at the entry point of the application, i.e. the Global.asax file
- The Timetable domain object will send an event to the mediator if one or more load tests have been inserted or updated
- The mediator then calls upon each registered subscriber to handle the message in some way
The goal is to keep the mediator class as independent as possible. It won’t have any knowledge of which concrete domain event handlers – i.e. subscribers – have registered themselves and what they do with the message. You should never need to check the type of the subscriber and try to do any action on its behalf. The bottom line is that the domain and its subscribers will never “meet in person”.
In the previous post we wired up the decorator in DefaultRegisty.cs:
For<ITimetableService>().Use<TimetableService>().DecorateWith(i => new TimetableServiceWithEmail(i, new FakeEmailService()));
We won’t need the decorator so you can comment out this call. You’ll have it there for future reference.
Then add a reference to the WebSuiteDDD.SharedKernel layer from the WebApi layer. We’ll place the mediator in the shared kernel and the web api layer will need to access it.
That’s enough of the theory. In the next post we’ll see all this in action.
View the list of posts on Architecture and Patterns here.