When to Use a Message Queue or a Bus in Domain Driven Design

Messages queues or message bus are systems that allow communication between senders and  receivers.

Remember that in Domain Driven Design we have two main types of messages: commands and events.



Recently I attended a meeting regarding Domain Driven Design and reactive architectures. The presenter was talking about communication between microservices and showed a slide where there was a message bus between publishers and subscribers. It was something like this, and forgive me for my painting skills:


I was thinking, ok, I understand why would I put a bus between an event publisher and event subscribers (i.e: event handler) for communication purposes because:

  • Events are published and can be handled by zero, one or more subscribers. So a bus makes sense because it decouples publishers from receivers and because multiple microservices could subscribe to the same event to know that something has happened.
  • It is interesting to communicate in an asynchronous way, so when the publisher puts an event in the bus it does not care who will listen to it or when, it just cares about the event being queued and ready to be received by any subscriber interested on it and as soon as it publishes the event it will move on and forget about it.
  • It possibly allows for a retry logic out of the box, so the publisher knows that sooner or later (if the subscriber is temporarily unavailable) the event will be received
  • It allows for an easy way to scale the application, because if a new microservice is added and needs to subscribe to certain event, we don't need to modify the publisher's code at all.
But then I could not help but wonder why would we want to put a bus between a command "publisher" and a command subscriber (i.e: command handler) because:
  • Commands are handled by one, and only one, command handler. Think about it, if you send a command you know exactly where it will be its handler: in the domain you want to perform an operation on. So no need to decouple. If you send a command to AddPlayer you know it will be handled by some kind of Game domain because that's the place where adding a player in a specific way should be understood. I don't mean that other domains or microservices don't understand the concept of player, but in the exact same way? with the exact same attributes? with the same aggregate Id? I don't think so.
  • While "publishing" a command in an asynchronous way is certainly not forbidden, why would you want to do so if you know exactly that there should be only one command handler? Would not you prefer to get a response message with some details about how this command has been processed? Maybe an aggregate Id, and an aggregate version in the response could be very useful for the client that sent the command. If you use a bus for commands you lose the benefit of knowing whether this command has, at least, attempted to be processed. Of course there are some other ways to know that, but it would add complexity to the client and not much benefit. 
  • A retry logic is useful in some scenarios, in other scenarios it is useless. Imagine you want to add a player to an ongoing game so you put an AddPlayer command on a bus and the Game is unavailable because the service is temporarily down. Thanks to the bus you could have an out of the box retry logic that will ensure that eventually the command will be delivered to its command handler. But whay if the game becomes available only after one hour? Maybe by then you don't care about the game anymore and you would have preferred to know immediately that the game was unavailable.
  • Finally, the scalability benefit that a bus provides for events is lost for commands if using a bus. As I mentioned, you don't want to have multiple command handlers for the same command. A command should be one single domain's responsibility.
At the end of the presentation I asked what are the benefits of having a message bus between commands publisher and handlers. The answer was, as I suspected, that in their application they had multiple microservices handling the same command. The presenter explained that one microservice was, for example, a Wallet, and another microservice was a Gambling Portal. He said that whenever you send the command to PlaceBet, both microservices had to receive this intention so that the bet was placed and the money was deducted from the wallet.

That is not a good event driven architecture for mainly two reasons. Firstly it has multiple handlers for the same command and that should never happen. Secondly, if the Wallet does not have enough money there is a risk that the bet has been already placed and then the system must do the necessary changes to cancel the bet for insufficient funds in a very complex way.

A better design would be having the Wallet microservice subscribe to BetPlaced events so that whenever a bet is placed the Wallet knows that needs to deduct certain amount of money from the user's balance. You would still need to handle situations where a bet was placed without enough funds, but at least you are using events to communicate between microservices.

A much better design would embrace even more the event driven communication and would have a command PlaceBet handled only on the Gambling Portal. This would produce an event such as DraftBetPlaced that would reach the Wallet because it is subscribed to it. The Wallet would then make sure the user has enough funds for that bet and issue a MoneyLocked event that would be received at the Gambling Portal because it is subscribed to it and this would confirm the draft into a BetPlaced, which would then be listened by the Wallet that would take the money locked from the user's balance and issue a MoneyDeducted event.

Then, and only then, an invoice could be created. This flow of events, which of course is just an example, have allowed the two miscroservices to ensure they are always at a consistent state and to communicate through meaningful events.

Comments