For some, the ideal picture of a modern application is a collection of microservices that stand alone. The design isolates each service with a unique set of messages and operations. They have a discrete code base, an independent release schedule, and no overlapping dependencies. As far as I know, this type of system is rare, if it exists at all. It might seem ideal from an architectural perspective, but clients might not feel that way. There’s no guarantee that an application made up of independently developed services will share a cohesive API. Regardless of how you think about Microservices vs. SOA, services should share a standard grammar and microservices communication is not always a design flaw. The fact is, in most systems you need to share data to a certain degree. In an online store, billing and authentication services need user profile data. The order entry and portfolio services in an online trading system both need market data. Without some degree of sharing, you end up duplicating data and effort. This creates a risk of race conditions and data consistency issues. At the same time, how do you share data without building a distributed monolithic service instead of a micro? What’s the effective and safe way to implement microservice communication? Let’s take a look at a few different mechanisms. First, we’ll go over different sharing scenarios. Depending on how you use the data, you can share it via events, feeds, or request/response mechanisms. We’ll take a look at the implications of each scenario. Then, we’ll cover several different mechanisms for microservice communication, along with an overview of how to use them.RESTful services are easy to create and maintain. In circumstances where both external clients and internal services need to access the same information, “stacking” APIs makes sense. Reusing interfaces avoids duplication. Even if you don’t make the service available to external clients, you can reuse the data model, which means higher efficiency. But REST is not without risk. While creating a RESTful service is easy, implementing a robust one can be difficult. Any service that sits at the center of a system needs to be reliable and resilient. Implementing a fault tolerant REST service requires more effort than for messaging. Since RESTful services are synchronous, they are not suitable for sharing data when low-latency is essential.
Sharing ScenariosWhat data do your services need to share? How often do you update that shared data? Is one service the primary provider of the data? Does the nature of the data suggest the creation of a dedicated facility to share it? Before deciding how to share data, it’s essential to identify the information you’ll share and how each service will use it. You need to ensure that sharing data does not result in tightly-coupled services. It’s one thing to have microservices that communicate with each other. It’s another to build a distributed monolithic service. Microservices are often developed by different teams, and the teams need to communicate if the services are going to share data. A system such as domain-driven design can often help with defining boundaries that make sense at a business level. There are many different ways to slice up and categorize interactions between services. Let’s break them down into three broad categorie.
Data FeedSometimes the shared data is a stream of updated or new records. For example, capital markets are often represented as a stream of prices and transactions. Another example is a feed of orders for an e-commerce business. A third example is a stream of inventory changes. Services most often consume data feeds as a “real-time” stream of records. A stream requires clients to cache the data that they are interested in, which can be expensive. However, a significant advantage of feeds is that the client and server are very loosely coupled. They only need to share data types and message formats.
Request-ResponseAn alternative to a stream is request-response messaging. When a service needs external data, it requests it from the data provider. By requesting the data when the service needs it, the need for a cache is eliminated. But, it adds latency to transactions that need shared information. The advantage of using a smaller cache is often greater than that latency, though. Depending on how you implement it, request-response can create a tight coupling between data clients and servers. In addition to sharing data formats, they may need to communicate directly with each other.
Domain EventsDomain events are similar to a data feed, but with additional application-specific semantics. They represent a change in application state. Some services process the events, depending on the context of the event and the role of the receiver. For example, a sale in an e-commerce application consists of several steps, each of which may result in an event. So, you can break a sale down into steps.
- Add the item to a cart
- Input a coupon code
- Select a shipper
- Submit a credit card transaction to a payment processor.