Microservices is an architectural style that structures an application as a collection of independently deployable services. The use microservices can accelerate the development of innovative solutions by enabling the rapid and reliable delivery of new functionality – but there are challenges in doing so, especially when your microservices are REST based. When you decompose a monolithic application into a set microservices, you need a way to tie them together. Many businesses do that with just REST, but implementing REST-based microservices introduces challenges that threaten to reduce or eliminate the benefits you were looking for in the first place.
What I propose is this: use REST when it’s necessary (when an interaction needs to be synchronous) and consider using event-driven architecture when it’s not. Here are five reasons why implementing REST based microservices can cause you more pain than you signed up for:
Let’s examine these challenges in implementing microservices in more detail.
With REST, Service A knows the details about Service B, including how to connect, how to handle errors, and business logic about how to handle responses from Service B.
This tight coupling threatens to turn a set of microservices into a monolithic structure like the one you’re trying to leave behind.
By contrast, event-driven architecture (EDA) uses a publish-subscribe model. Service A publishes an event to an event broker… and that’s it. Service A’s job is finished. The event broker handles the details of where to send the event and which microservices are subscribed to the event, while guaranteeing that the event won’t be lost in transit.
Modern architectures emphasize horizontal scaling, i.e. creating new instances of services instead of adding memory and CPU to a single server. However, the tight coupling of REST means that if one service (like service C in the example below) takes longer to finish, you must also have to scale the number of A and B instances or you won’t see an increase in performance.
By contrast, EDA’s loose coupling lets you scale each microservice independently. If Service C is the slowpoke, add instances to hit your performance numbers. If Service B is a speed demon, keep the instance count low and save on computing costs.
The move from REST’s tight coupling to EDA’s loose coupling seems like a minor shift, but it makes a huge difference. A loosely coupled architecture gives you a degree of flexibility that can help you overcome challenges that might otherwise prevent innovation and widespread use of real-time data.
Unexpected errors are difficult to recover from in REST-based microservices. In the example below, Service B makes a RESTful call to Service C, but crashes before Service C sends the reply. Service C is unaware of the crash and commits the changes. Service A receives a timeout and rolls back. What happens with Service B depends on the manual business logic in its code.
As a result, all three databases may have different information. Getting them consistent again can require manual intervention, and introduce lost time, confusion and inaccurate results.
EDA gives you a different option: eventual consistency. Eventual consistency means that if you can guarantee no events are lost, the underlying datastore will eventually reflect the same value. In cases where it’s appropriate, eventual consistency can seriously simplify your architecture and error handling.
As soon as Service A publishes the event, it commits the information to its database. The event broker distributes the event to individual queues for both Service B and Service C (and any other interested services). Once the event lands on the queue, it’s persisted, and the event broker won’t remove it until the service confirms that it’s safe to do so.
This offers powerful protection in the case of a failure… like the one Service B is about to experience. But even though Service B fails, it didn’t give the OK to remove the event, so it stays in the queue, waiting for Service B to be revived. Meanwhile, Service C commits the change.
When Service B comes back to life, it picks up the event again and commits it to the Service B database. Without manual intervention, and as quickly as possible, the databases are consistent.
You can learn more about eventual consistency in the blog post Eventual Consistency in Microservices and My Front Yard: Event-Driven Architecture vs. REST.
With REST-based microservices, adding new services is like adding another link to an ever-growing chain. Your solution’s response time gets slower and slower, and consumes more resources along the way. Adding services is the only way to get new functionality, so you have to settle for the increased latency unless you try a different approach.
EDA makes it easier to run microservices asynchronously and in parallel. That’s especially true with applications that can work with eventual consistency. Each microservice (like Service B and Service C below) get a copy of the event, which they can process at their own speed. That way adding new services doesn’t mean your customers wait longer for results.
Having to modify the behavior of an existing service just to add a new service demonstrates REST’s tight coupling. Adding Service C means changing Service A’s existing code to add routing and error handling, which of course means that both Service A and Service C need to be retested. You also run the risk of introducing bugs into code that’s working. If it ain’t broke, don’t touch it!
In event-driven systems, the event broker is home to and responsible for all connectivity details, error handling and business logic. When Service A publishes an event the broker makes its delivered to zero, one, two, or twenty different microservices that are subscribed to receive it. That loose coupling means when you add Service C you only need to test Service C and how it interacts with the broker. That way application teams can focus on their core competencies and areas of responsibility, instead of worrying about dependencies.
If you want to know more about how EDA can streamline your microservice deployment process, check out How to Automate Your Microservices Deployment Strategy.
REST is a one-to-one, synchronous, request/reply protocol. That’s all it can do. When you first start working with microservices, when you’re looking at a single connection between Service A and Service B, it’s tempting to link them with a point-to-point REST connection.
But innovation is inevitable (or at least it should be), and soon Service C needs to connect to Service A. And then over time as you add new cloud, IoT, big data, and high-speed caching services to the mix, many of them will need that same data from Service A.
By this point, you’ve outgrown REST, but you’re stuck with the one-to-one, request/reply model. Unless you rip and replace it with something more flexible.
I suggest you build something more flexible from the start. Using EDA right out of the gate doesn’t just give you the benefits of loose coupling out of the gate, it makes it way easier to add new services over time.
What’s more, many event brokers support multiple protocols, so you can pick the right one for each and every use case that comes up. Sending events to a mobile device? Use MQTT. Got some analytics to run? Connect via the Kafka API. Connecting event brokers from different companies? Chances are they both speak JMS.
Event-driven architecture can help you solve many of the challenges inherent with implementing REST-based microservices. While I wouldn’t recommend it be used in every situation, it’s a crucial architectural tool that can help drive real-time data innovation, so it’s important to have in your toolbox.