Just as a bit of a preamble, today is the first of what will hopefully be many days of me knocking out my blog backlog by actually taking an hour lunch and bringing my laptop to write. Unfortunately I currently have a backlog of over 40 posts … Hopefully in a few weeks that number will come down a bit. This is the first in many posts about DDDD (the D is for Distributed), oddly when talking about it here the extra D seems to make sense just about everywhere so I will leave it to you to figure out which one it is…
I recently posted about using messaging in the domain in Mocks are a Code Smell. One item that I left out of that post was when one should be using a message or a direct call. I have over time found boundaries that work very well for me at least 😉
DO use a message any time you call a service from any where.
Services are the most natural (and easiest implemented) boundary for messaging. Services generally have a clear interface that is easily changed to become messages instead. It is also quite easy to route these service calls as you can usually just route the messages based on the type of message. Ex:
When I have a SendEmailService I can route all SendEmailRequest messages to it, in terms of dispatching; this is extremely simple and has about the same overhead as using DI but I have greatly reduced my coupling. As has been seen in Mocks are a Code Smell it can also make my tests more explicit.
When moving to DDDD this allows you to load balance and/or distribute services in a transparent manner.
DON’T message to your repositories …
Not a lot of explanation here … Just don’t do it. I will go into more later (in another post) about how repositories and messaging can interact.
DO use a message anytime you are in an aggregate root and make a call out of the aggregate root
DO use a message to access functionality in or under an aggregate root.
NEVER use a message when you are within the same aggregate
Aggregate boundaries are one of the most important messaging boundaries when introducing messaging to a domain. Aggregates should not be considered groups of related objects but in terms of messaging should be treated as a single functional unit (update: this also applies to little helper functions etc that the aggregate calls).
This becomes especially true when you move towards DDDD. Aggregates exist within the mesh in their entirety, server 4 will either have the entirety of the Customer aggregate for customer 17 or it will have none of it, it will never hold “parts”, this does not mean that the entirety of the aggregate will be loaded (it may still support lazy loading). Not only does this greatly simplify the routing of messages within the mesh it also helps to prevent chatty interfaces.
All messages coming into an aggregate should be destined to the aggregate, never to a piece of the aggregate. The aggregate should be responsible for coordinating the messages it receives to its contained parts. By dispatching messages to aggregates we have less information to store for routing purposes, locate by aggregate. This also fits directly into the concept of an aggregate in DDD in that it should be controlling access to all objects within its boundary.
DO use a message anytime you cross a context boundary
DO make messages between context boundaries a format that is specific to neither Bounded Context
Consider messaging between Bounded Contexts to be a key Integration point.
Context Boundaries are one of the places that messaging shines in any system although this could be included in Domain Service in many cases because interactions generally sits behind a service. Messaging introduces what is essentially a very thin and loosely coupled Shared Kernel (or perhaps its more of a Core Domain) between the Bounded Contexts. It is key that when you develop these messages you treat them as the global interface to the originating Bounded Context, any other Bounded Context will be accessing the originating Bounded Context through this interface.
The anti-corruption layer when introducing messaging across Bounded Contexts is especially interesting, there are two common ways of implementation. In either implementation the originating Bounded Context translates its own concept of data to the shared messaging contract. For many cases with small amounts of interaction this is actually enough to keep the contexts decoupled and the receiving Bounded Context can deal directly with the messages.
If this becomes a bit burdensome it can be beneficial to have the receiving bounded context translate the shared messages it receives into its own concept of the messages.
You may use messages for talking to your application services.
Since if you follow the rules above your application services will use messages to talk to the domain there may or may not be a gain in using messaging to talk to your application services. One place that it can be a gain is if you are sharing your API with others as your application services would generally match up better with a SOA type API.
There is another side of this discussion and that is you message TO your application services and ONLY to your application services (i.e. each instance of the application services is its own unique instance of the domain). This can work very well if you want your domain to be primarily Request/Response but it begins to fail very quickly if you want to do more pub/sub style interactions with the domain objects that live behind your application service facades.
I have actually been liking lately the idea of distributing application services to all clients and having those application services generate messages to the other pieces of the system. This can at times be chatty but later we will discuss ways to work around that.
to be continued …