Microservices — Synchronous vs Asynchronous Architecture
The microservices architecture has now become the standard way of delivering software. There is a significant nuance with how it is implemented and while both are difficult to manage one of them can become impossible to manage as the application begins to scale, can you guess which one?
Synchronous is quite straightforward with every feature making a call with other services directly and making requests to a database. Some features may or may not rely on other features or services but ultimately a complex web of interdependency is created and ultimately each feature has its own service to satisfy its need. Below is a simple synchronous-based Webservice.
In this example, all the services communicate directly with other services to get their needs met and while this would suffice for a small application it ultimately becomes impossible to manage with larger applications that end up with a multitude of interdependencies impossible to manage and maintain.
Asynchronous-based communications rely on indirect communications usually with an ‘Event Broker’. The feature that wants a service creates an event that is picked up by an Event Broker which then sends it to the relevant feature or a multitude of features to be worked on. The benefit is that there is no direct dependency on other services and it’s super fast. The downside though is data duplication and its more complex meaning is harder to understand. This solution is still not perfect as the event bus is a central component although in the example below the application will still be able to have posts created using the posts service even when the event broker is non-functional.
With the synchronous architecture, each service could have its own database and if a service goes down or is stopped for some other reason it's vital that the data it relies on is not lost. There are typically three ways this challenge can be met.
- Synchronous requests — The service/feature that is out of sync makes a request to other services for the missing information. This can result in a lot of information being requested from services that may not be able to provide all their data. This ultimately means we will be implementing synchronous processes which are ultimately inefficient and introduce interdependencies we are trying to avoid.
- Direct DB access — This is when the out-of-date feature implements the ability to obtain the data it needs directly from the database of the other features which again introduces interdependencies and potentially the need to query different types of databases.
- Store events — This solution feeds off the implementation of an event bus in an asynchronous solution. All events are stored in an “Even Bus” which will have an “Event Bus Data Store”. As a result, the newly introduced service will only need to obtain the missing data it needs from the data store in the event bus. The downside though is that there could be quite a large amount of data in the data store. This however is the preferred solution of all three.
Ultimately when implementing microservices there are a lot of factors that affect what level of interdependency will exist which ultimately boil down to the risk and impact. Smaller applications will get away with synchronous architecture but the bigger the application grows with the addition of new services and features the more the benefits of the asynchronous architecture.