DDDD: Didn't You Get The Message?

Greg has generously shed some light on a critical aspect of DDDD: How do I get the command (or event) message into the aggregate root?

First a small amount of background. When a message arrives for consumption, typically a message handler like MapMessageToAggregateRootHandler will be responsible for loading the aggregate from the repository and pushing the message into the aggregate root. (Note: The messages handled by this handler all have a GUID property representing the aggregate ID which the handler can use to load the aggregate from the repository.) From a high level, the concept is easy enough. But what about implementation specifics? What does the code actually look like to get the message into the aggregate root?

Originally I was thinking that the aggregate root would somehow need to expose a method for every single type of message (command or event) that it could consume. This could work, but it would make the message handler bloated and tightly coupled to the aggregate root.

Then I thought about having a single HandleMessage(IMessage msg) method on an aggregate root. This is better (in some respects) because at least the application service is protected from the internals of the domain. But still it's a big smelly method! A single HandleMessage method with a massive switch statement is just the kind of procedural code we're trying to get away from.

The Elegant and Simple Solution

Greg informed me that his aggregates explicitly implement an interface of:

IConsume where T : Message

Here's the beauty of the solution. Your application service would load the aggregate from the repository. It would then cast the aggregate to IConsume and it would then push the message into the aggregate through the Consume() method of the IConsume interface. Brilliant!

The reason this works so well is:

  1. The message handler is completely decoupled from domain-specific methods.
  2. The aggregate root doesn't need a single point of entry for all messages. Each message comes through its own point of entry.