The first edition was published in 2020, and with the pace of change being as brutal and unforgiving as it is, I started making notes for the second edition within a month of finishing the manuscript. The overall structure has remained the same, but I go far deeper into the different topics in the 2nd edition. There are also more visuals and a couple of new topics. This series of articles provides a summary of each of the chapters with some personal afterthoughts. Serverless Beyond the Buzzword 2nd edition can be purchased here: https://link.springer.com/book/10.1007/978-1-4842-8761-3
Written article continues below the video
Chapter 9 dives deep into the technology of Serverless architecture, looking at specific serverless component designs, patterns, best practices, and many examples.
It starts with challenges and mitigations, providing technical solutions to some of the challenges covered in Chapter 1 and additional technical challenges.
For the vendor Lock-In challenge, it is possible to make a cloud-agnostic solution that can run on different clouds, but to achieve this, it needs to be built for the lowest common denominator using only the services and features available across both cloud providers. For example, instead of using the proprietary service AWS Cognito for authentication, we will need to run a third-party authorisation tool on a container that can be deployed to both cloud environments and provide the same authentication experience. This increases the complexity and maintenance of the solution, so while cloud agnostic, it is not ideal from an optimisation perspective.
AWS: Microservices and APIs
In Amazon Web Services, the managed service Lambda is used to host our microservices. Lambda is what we call Function-as-a-Service (FaaS) because we upload function code, and everything else, such as redundancy and scaling, is fully managed. We are billed for each request and the milliseconds that the function is running. On AWS, we sometimes also call microservices Lambdas or Lambda functions.
Lambda has tight integrations with many other AWS services, such as DynamoDB, CloudWatch logging, API Gateway, and many more. Access is typically managed through IAM policies.
Lambda runs as a fully managed AWS service, and the microservices, by default, will run in an AWS-managed VPC. Access permissions and security are entirely managed through IAM role policies.
It is not uncommon, especially in larger enterprises, to see a requirement for Lambda microservices to run in a VPC managed by the organisation. Typically, this is related to security policies requiring subnet segregation. However, adding Lambdas to a VPC creates some additional complexity. There is a slightly higher cold start latency, security groups need to be configured and managed, and a proxy or NAT may be required – neither of which is Serverless.
Lambda integrates with AWS-native deployment services such as CodePipeline to quickly test, build, and deploy new microservice versions. CloudFormation, or at least another IaC templating language, can and should be used to configure a Lambda microservice and any integrations and permissions.
API Gateway is possibly the most commonly used service with Lambda, at least user-facing solutions. It acts as the front door for applications to access microservices and provides a means to manage, monitor, and secure access to APIs. API Gateway is a fully managed cloud service that does not require code development, meaning that developers can leverage Serverless capabilities for significant productivity gains while reducing operational overhead costs.
Coupling is the relationship between two components in an application that defines how closely they work together and depend on each other. Decoupling is when we seek to reduce that dependency without impacting the output or quality of the component. This is something that microservices are especially suited for as they are, by nature, independent and self-contained.
The reason for decoupling is to improve fault tolerance and enable each component to perform its respective tasks independent of the performance of other components in a workflow. Self-healing and automated error reporting can be designed into a decoupled workflow by monitoring each component and responding to events such as failures.
The main requirements for decoupling components are that the producers (components sending a result event) and subscribers (components triggered by the event) agree on a standard schema for the messages and use the same message broker. On AWS, we commonly use four services for decoupling microservices:
- Simple Queue Service (SQS) is where tasks are added to a queue for processing.
- Simple Notification Service (SNS) sends notifications in various formats and over various channels to one or more endpoints.
- EventBridge can filter events and send notifications to specific AWS services based on the event content. EventBridge can also schedule future and recurring events.
- Step Functions can manage complex workflows with multiple steps, parallel paths, user approvals, and more.
Event-driven architecture is a different way of designing Serverless microservices that represent steps in the business flow or process instead of technical features. The differences may seem subtle, but it makes microservice architecture considerably more easy to understand and to map the user stories or business requirements to technical requirements. Imagine a bank customer dropping a check into the deposit box. Instead of waiting for the manager to instruct them, the staff will take the initiative to process it automatically. This is similar to our microservices, reacting to changes in the environment.
For application teams, when integrating with existing services, they simply subscribe to the event router instead of having to write integration code. Improved reliability can be achieved since services are now less reliant on one another through decoupling. Services can fail, and the workflow will continue to run – if that makes sense for the use case. If not, then we can rely on the usual automatic retries and application team notification, followed by continuing the workflow where it left off once the issue is resolved.
Asynchronous design pattern
Werner Vogels specifically called out this pattern in his re:invent 2022 keynote. Usually, API services use a synchronous (sync) design, which receives a request, processes it, and returns the result within the same connection. If the request takes a while to process, the client must wait until it is done.
An asynchronous (async) design receives a request, can optionally perform some validation on it, responds to let the client know that the request was successfully received, and then ends the connection. The client does not wait on the connection for the actual request result. The back end will process the request and take the time it needs. Any response that may be required will be returned via a new connection, which could be an entirely different channel.
The fact that asynchronous requests run independently in the background also makes them great for parallel processing. With parallel processing, we split a request into multiple smaller requests and execute them simultaneously. Typically, results are merged once all parallel processes are complete.
Other architecture patterns and services
Chapter 9 covers several other areas, such as:
- Containers, comparing them to Lambda microservices and when it makes more sense to use a container.
- AWS Edge and Internet of Things (IoT) services
- AWS-managed machine learning services, such as Rekognition that can identify all kinds of objects and scenes in images and videos
- AWS ledger services and its fully serverless call centre offering
Lastly, chapter 9 closes with a list of tips to consider when designing Serverless architecture.
Check out the book's mini site for more information and ordering here: https://serverlessbook.co