Whether organizations turn to microservices to solve issues with existing applications or build greenfield applications, creating applications that consist of many single purpose microservices introduces the need for those individual services to interact in real-time to provide value to the end user.
Today REST is most commonly used for these interactions, and it works fine for some situations, but as discussed in my previous blog post there are many advantages to using messaging instead, not the least of which is the ability to use a wide range of synchronous and asynchronous message exchange patterns.
Note: I explored this topic on a deeper level in my latest guide:
These patterns, such as publish/subscribe, request/reply, one-way notification and multi-request/single-response have been a staple of enterprise architecture for many years, and they are ideal for microservices since they are inherently distributed. To prove this point, lets look at problem.
The problem I have chosen to solve is near and dear to my heart (as I hope it is to yours!). Imagine you’re at a party where the supply of beer has begun to dwindle? Wouldn’t it be awesome if someone would magically appear and restock the supply? Well, let me present to you the Solace Beer Online Store, which I’ll call Sol-Beer. It makes this dream come true by combining the convenience of an e-commerce platform with a rapid delivery platform. Not only does this use case solve a real-world problem, it proves out many of the common challenges developers face encountered when creating a microservices architecture. Without further ado, lets jump in!
The problem faced here can be broken down into a set of discrete components, which of course must interact to provide value (i.e. beer). The components are:
With the components defined, we need to orchestrate these components (i.e. services) so you can be the hero who solves the party’s beer problem.
So let’s say you’ve read that microservices are synonymous with REST, or assume that the use of an “technology agnostic protocol” means REST. Because REST over HTTP is inherently synchronous and only natively supports request reply, the orchestration of services would look like this:
The customer initiates the order using the Sol-Beer Web App, which acts as the orchestrator by synchronously invoking the Inventory Manager, Billing and Logistics services before returning success or failure response to the customer. If we further decompose these interactions into a sequence diagram, it reveals some serious flaws in the architecture:
Here’s what’s wrong with this architecture:
It is apparent that if we were only to use REST as the communication protocol between our services, we would have a mess and not realize many of the benefits of microservices architecture.
To leverage the power of event-driven microservices you need to shift your thinking from “invoking services” to “initiating and capturing events.” Think about systems publishing events that can be consumed by 0 or more downstream services and interpreted within the context of each service. The amount of flexibility and decoupling this provides is astounding. Let’s take another look at the architecture behind Sol-Beer with that in mind.
Look what happens when we add messaging to our microservices architecture – the services are fully decoupled and no longer know about each other. Also, the responsiveness of the Sol-Beer WebApp is determined solely by the Inventory Manager because all other activities are only called later. Our site is now better able to handle large demand spikes because the only services that need to be scaled are the WebApp and the Inventory Manager. The other services can be scaled to average load as they no longer impact the customer’s order experience. Let’s look at the message exchange patterns that make up our new architecture.
Request/Reply can easily be implemented by using messaging, but why would you use messaging vs REST for Microservices?
Remember when I said you need to start thinking think about events instead of services? Let’s use that concept for when the client places his order to our web app. Our business processes dictate that the inventory needs to be reserved in order to return a message of “your order is being processed.” The web app should only know that an order event has occurred and been confirmed. It doesn’t need to know if that confirmation comes from the inventory manager, billing or both.
Here you see how the generated event can be produced and consumed by the inventory manager, and a response event can be sent back to the customer once it’s confirmed. There are a few key concepts here:
Since the Sol-Beer store produced an event, this event can be consumed by 0 or more recipients. In our case, it is consumed by the Inventory Manager, but what if we wanted to have those events also consumed by an analytics engine? This is where the big benefit lies as you can add these kinds of capabilities without touching the Web App or Inventory Manager. Microservices are all about the ability to create new capabilities faster after all.
The combination of messaging and event-driven architecture really begins to shine when events can be sent in a “fire and forget” way. The one-way notification pattern is a great example of this mode and was performed in the previous step when the inventory manager sent a message indicating that an order’s set of products were reserved. From a process perspective, one service that would be interested in those events would be billing. The below diagram shows the billing service consuming those events and acquiring funds from the customer.
A common challenge with microservices architecture is the need to avoid XA transactions which aren’t supported. From the business perspective, if the credit card bounces, we need to cancel the order and remove the reservation on the product so others can buy it. By making use of the concept of “eventual consistency” we can avoid XA transactions entirely. The key to eventual consistency is that the events must never be lost, or the system will be in a perpetual state of inconsistency. Unlike REST, messaging can guarantee the delivery of information, even in the event of component or network problems.
In our case, if there were no funds, the Inventory Manager needs to listen to these events so it can undo the product reservation and let the customer know their order has been cancelled due to a payment problem. The key is that the Billing service does not know what steps need to be taken to roll back the order; it simply produces events indicating “paid” or “not paid.” Clean and elegant!
If you have ever used Uber or Lyft, you will have seen that the drivers react immediately if their phone dings with another customer in the area needing a ride. Those notifications go to all drivers in the area, and it’s the first to respond that wins the ride/business. This is exactly how the Sol-Beer store delivery mechanism is intended to work. The ability to send an event to multiple clients and receive multiple responses is key to this use case and messaging is a perfect enabler. This diagram illustrates how this can be implemented:
Once the Inventory Manager has fulfilled the order, it sends an event to the topic order/ship/<order number>/<delivery zip>. If you’re wondering why we would include delivery zip in a topic name, that’s because if each car subscribes to events for a certain geography, we have a clean routing mechanism so drivers won’t be overwhelmed by events outside of their area. In this case, the Logistics manager receives ship notifications and sends out an event indicating that it’s looking to locate a delivery vehicle.
Until now we’ve been using the JMS API as that is commonly used for communication between applications within an enterprise, but for a car or mobile device MQTT or WebSocket is more appropriate. Solace makes it easy to use the right tool for each job by supporting many open APIs and standard wireline protocols.
Each car or mobile device presents the driver with a notification that an order is ready to be delivered, and they can choose to accept or deny the opportunity. As stated before, the Logistics service only needs a single accept and for fairness selects the driver who responded first.
The use of messaging is most commonly associated with the Publish/Subscribe message exchange pattern. Publish/subscribe is an element of some of the message exchange patterns we have explored, but it can also be used directly to enhance the user’s experience. In our case, wouldn’t it be great if the customer could track the delivery vehicle, in real-time, that contains our order? As farfetched as it sounds, many believe that these types of capabilities will be the future of e-commerce. For example, what if you opted into a FedEx or UPS service where they could deliver a package to your parked car while you are at work. In our case, the user will navigate to our web app and using a mapping service such as google maps will be able to track his beer in real-time!
Because we used publish/subscribe these events could be consumed by multiple components based on their interest. There is no reason for the Web App to consume these events unless a user navigates to that screen. Logistics, on the other hand, may wish to always receive these events to ensure the driver follows the predefined route and that they do not speed excessively.
Hopefully the Sol-Beer store will be implemented one day by a company and fulfill my dreams! In the meantime, I hope that you found it an interesting way to look at the various ways message exchange patterns can unlock the full benefits and value of microservices architecture. Consider adding these patterns to your tool box and experience the real benefits provided by event-driven microservices.