AWS CloudFront — This service works like a proxy as well as a CDN for application requests. The SSL connections we establish between the user’s browser and CloudFront where it will forward the requests to the application backend or frontend depend on the request path. For instance, if the browser request for a frontend path like static content (HTML, CSS, JS), CloudFront can route it to Amazon S3 and serve the static content. If its a browser request for an API resource (e.g.,/API/ ), CloudFront forwards it to the backend of the application.

AWS API Gateway — This works like the event listener for API resource requests received over the web and triggers Lambda functions as required. These are requests typically forwarded from CloudFront.

Amazon S3 — We use S3 for static file storage like images and other frontend assets. Beyond this use case, S3 could also capable of hosting the frontend of a web application, if it consists of static files like HTML, CSS, and JS.

AWS Lambda — The application code that handles the API backend functionality is uploaded here. Lambda provides the underlying compute platform. The Lambda functional also capable of communicating with the database for both data retrieval and data updates.

AWS DynamoDB — This is a NoSQL database service offered by AWS to store and retrieve data.

AWS Cognito — A fully managed, identity solution where it allows user authentication and authorization using OpenID Connect.

Can we use serverless for Microservices?

Let’s assume the application has grown over the years introducing services with different business capabilities. If we go along with the same technology stack, we can implement each of the Microservice using APIGateway, Lambda, and DynamoDB. However, the actual technology stack might vary depending on the exact business requirements.

But is it that simple to introduce Microservices to a growing application as shown in the diagrams? The answer is a big NO!.

The diagrams look simple since we have left out a basic functionality which is the interaction between Microservices.

In the real world, these Microservices need to interact with each other to provide business functionalities. On the other hand, these distributed systems introduce further challenges for the application.

Microservice Direct API Calls

This approach is the easiest way of interactions between Microservices, where one Microservice calls the other through its API interface. However, this approach tends to fail when there are more than two services involved in single business functionality. This failure happens because we need to prepare for any partial failures without jeopardizing the data consistency across different Microservices.

Microservices Communication using Async Messaging with Pub-Sub using AWS SNS

This approach is the easiest when it comes to propagating data changes across various Microservices. The best thing with this approach is that it doesn’t require one Microservice to know about the other. They only need to add a listener for the data changes within each Microservice.

To implement this, we will need to find a publisher, subscriber middleware in AWS. The simplest and effective candidate service available for this is AWS SNS. AWS SNS provides support to declare these topics, publish and subscribe to them using AWS Lambda. When we publish a notification to SNS, it will make sure the message will be delivered to all the listeners, having support for retry.

Transactional Behaviors between Microservices

But what if the changes are transactional? To provide this functionality, we need further middleware support. We need assistance both to propagate the transaction between services and to rollback in case of a problem.

The first option that goes out of our way is using distributed transactions where they are costly, defying the purpose of building distributed systems in the first place. The best option at hand is to implement SAGA pattern across the Microservices involved in for the transaction. In simple terms, it is to do local transactions inside Microservices and use events to coordinate across Microservices with the help of a predefined state machine. However, this is easier said than done, without the help of middleware services.

When using AWS, you can model the transactions using AWS Step Functions to implement SAGAs. This approach reduces the complexity by allowing to define a state machine to handle the transaction. The AWS Step Functions are quite capable of coordinating the transaction as well as rolling it back if anything happens unexpectedly.

Summary

As you have seen, building full-stack applications using serverless is already becoming a reality. When it comes to Microservices, serverless goes further by providing a set of middleware to support Microservices interactions. These are crucial to implementing even the basic communication patterns for Microservices.

However, this article discussed only a few use cases where we need the support of serverless middleware. There are plenty of use cases that the AWS serverless services could handle when the need arises. E.g., If you are working with streaming data, you can use AWS Kinesis. If you retain the order of processing different messages, you can use AWS SQS.

Likewise, AWS ecosystems provide different building blocks to support Microservices. The pricing model AWS also facilitates choosing different design options where most of the pricing goes for the actual consumption of the service. The best example would be to use AWS API Gateway as a Micro-Gateway for all the Microservices where it would be a significant design decision in other cloud providers.

Learn More