Chapter 2. Architectures and patterns

This chapter covers

  • Use cases for serverless architectures
  • Examples of patterns and architectures

What are the use cases for serverless architectures, and what kinds of architectures and patterns are useful? We’re often asked about use cases as people learn about a serverless approach to the design of systems. We find that it’s helpful to look at how others have applied technology and what kinds of use cases, designs, and architectures they’ve produced. Our discussion will center on these use cases and sample architectures. This chapter will give you a solid understanding of where serverless architectures are a good fit and how to think about design of serverless systems.

2.1. Use cases

Serverless technologies and architectures can be used to build entire systems, create isolated components, or implement specific, granular tasks. The scope for use of serverless design is large, and one of its advantages is that it’s possible to use it for small and large tasks alike. We’ve designed serverless systems that power web and mobile applications for tens of thousands of users, and we’ve built simple systems to solve specific, minute problems. It’s worth remembering that serverless is not just about running code in a compute service such as Lambda. It’s also about using third-party services and APIs to cut down on the amount of work you must do.

2.1.1. Application back end

In this book you’re going to build a back end for a media-sharing, YouTube-like application. It will allow users to upload video files, transcode these files to different playable formats, and then allow other users to view them. You’ll construct an entirely serverless back end for a fully featured web application with a database and a RESTful API. And we’re going to show that serverless technologies are appropriate for building scalable back ends for all kinds of web, mobile, and desktop applications.

Technologies such as AWS Lambda are relatively new, but we’ve already seen large serverless back ends that power entire businesses. Our serverless platform, called A Cloud Guru (http://acloud.guru), supports many thousands of users collaborating in real time and streaming hundreds of gigabytes of video. Another example is Instant (http://instant.cm), which is a serverless content management system for static websites. And yet another example is a hybrid-serverless system built by EPX Labs. We’ll discuss all of these systems later in the chapter.

Apart from web and mobile applications, serverless is a great fit for IoT applications. Amazon Web Services (AWS) has an IoT platform (https://aws.amazon.com/iot-platform/how-it-works/) that combines the following:

  • Authentication and authorization
  • Communications gateway
  • Registry (a way to assign a unique identity to each device)
  • Device shadowing (persistent device state)
  • A rules engine (a service to transform and route device messages to AWS services)

The rules engine, for example, can save files to Amazon’s Simple Storage Service (S3), push data to an Amazon Simple Queue Service (SQS) queue, and invoke AWS Lambda functions. Amazon’s IoT platform makes it easy to build scalable IoT back ends for devices without having to run a server.

A serverless application back end is appealing because it removes a lot of infrastructure management, has granular and predictable billing (especially when a serverless compute service such as Lambda is used), and can scale well to meet uneven demand.

2.1.2. Data processing and manipulation

A common use for serverless technologies is data processing, conversion, manipulation, and transcoding. We’ve seen Lambda functions built by other developers for processing of CSV, JSON, and XML files; collation and aggregation of data; image resizing; and format conversion. Lambda and AWS services are well suited for building event-driven pipelines for data-processing tasks.

In chapter 3, you’ll build the first part of your application, which is a powerful pipeline for converting videos from one format to another. This pipeline will set file permissions and generate metadata files. It will run only when a new video file is added to a designated S3 bucket, meaning that you’ll pay only for execution of Lambda when there’s something to do and not while the system is idling. More broadly, however, we find data processing to be an excellent use case for serverless technologies, especially when we use a Lambda function in concert with other services.

2.1.3. Real-time analytics

Ingestion of data—such as logs, system events, transactions, or user clicks—can be accomplished using services such as Amazon Kinesis Streams (see appendix A for more information on Kinesis). Lambda functions can react to new records in a stream, and can process, save, or discard data quickly. A Lambda function can be configured to run when a specific number (batch size) of records is available for processing, so that it doesn’t have to execute for every individual record added to the stream.

Kinesis streams and Lambda functions are a good fit for applications that generate a lot of data that needs to be analyzed, aggregated, and stored. When it comes to Kinesis, the number of functions spawned to process messages off a stream is the same as the number of shards (therefore, there’s one Lambda function per shard). Furthermore, if a Lambda function fails to process a batch, it will retry. This can keep going for up to 24 hours (which is how long Kinesis will keep data around before it expires) if processing fails each time. But even with these little gotchas (which you now know), the combination of Kinesis streams and Lambda is really powerful if you want to do real-time processing and analytics.

2.1.4. Legacy API proxy

One innovative use case of the Amazon API Gateway and Lambda (which we’ve seen a few times) is what we refer to as the legacy API proxy. Here, developers use API Gateway and Lambda to create a new API layer over legacy APIs and services to make them easier to use. The API Gateway is used to create a RESTful interface, and Lambda functions are used to transpose request/response and marshal data to formats that legacy services understand. This approach makes legacy services easier to consume for modern clients that may not support older protocols and data formats.

2.1.5. Scheduled services

Lambda functions can run on a schedule, which makes them effective for repetitive tasks like data backups, imports and exports, reminders, and alerts. We’ve seen developers use Lambda functions on a schedule to periodically ping their websites to see if they’re online and send an email or a text message if they’re not. There are Lambda blueprints available for this (a blueprint is a template with sample code that can be selected when creating a new Lambda function). And we’ve seen developers write Lambda functions to perform nightly downloads of files off their servers and send daily account statements to users. Repetitive tasks such as file backup and file validation can also be done easily with Lambda thanks to the scheduling capability that you can set and forget.

2.1.6. Bots and skills

Another popular use of Lambda functions and serverless technologies is to build bots (a bot is an app or a script that runs automated tasks) for services such as Slack (a popular chat system—https://slack.com). A bot made for Slack can respond to commands, carry out small tasks, and send reports and notifications. We, for example, built a Slack bot in Lambda to report on the number of online sales made each day via our education platform. And we’ve seen developers build bots for Telegram, Skype, and Facebook’s messenger platform.

Similarly, developers write Lambda functions to power Amazon Echo skills. Amazon Echo is a hands-free speaker that responds to voice commands. Developers can implement skills to extend Echo’s capabilities even further (a skill is essentially an app that can respond to a person’s voice; for more information, see http://amzn.to/2b5NMFj). You can write a skill to order a pizza or quiz yourself on geography. Amazon Echo is driven entirely by voice, and skills are powered by Lambda.

2.2. Architectures

The two overarching architectures that we’ll discuss in this book are compute as back end (that is, back ends for web and mobile applications) and compute as glue (pipelines built to carry out workflows). These two architectures are complementary. It’s highly likely that you’ll build and combine these architectures if you end up working on any kind of real-world serverless system. Most of the architectures and patterns described in this chapter are specializations and variations of these two to some extent.

2.2.1. Compute as back end

The compute-as-back-end architecture describes an approach where a serverless compute service such as Lambda and third-party services are used to build a back end for web, mobile, and desktop applications. You may note in figure 2.1 that the front end links directly to the database and an authentication service. This is because there’s no need to put every service behind an API Gateway if the front end can communicate with them in a secure manner (for example, using delegation tokens; chapters 5 and 9 discuss this in more detail). One of the aims of this architecture is to allow the front end to communicate with services, encompass custom logic in Lambda functions, and provide uniform access to functions via a RESTful interface.

Figure 2.1. This is a rather simple back-end architecture for storing, calculating, and retrieving data. The front end can read directly from the database and securely communicate with different services. It can also invoke Lambda functions through the API Gateway.

In chapter 1, we described our principles of serverless architectures. Among them we mentioned thicker front ends (principle 4) and encouraged the use of third-party services (principle 5). These two principles are particularly relevant if you’re building a serverless back end rather than event-driven pipelines. We find that good serverless systems try to minimize the scope and the footprint of Lambda functions so that these functions do only the bare minimum (call them nano functions, if you will) and primarily focus on the tasks that must not be done in the front end because of privacy or security concerns. Nevertheless, finding the right level of granularity for a function can be a challenging task. Make functions too granular and you’ll end up with a sprawling back end, which can be painful to debug and maintain after a long time. Ignore granularity and you’ll risk building mini-monoliths that nobody wants (one helpful lesson we’ve learned is to try to minimize the number of data transformations in a Lambda function to keep complexity under control).

A Cloud Guru

A Cloud Guru (https://acloud.guru) is an online education platform for solution architects, system administrators, and developers wanting to learn Amazon Web Services. The core features of the platform include (streaming) video courses, practice exams and quizzes, and real-time discussion forums. A Cloud Guru is also an e-commerce platform that allows students to buy courses and watch them at their leisure. Instructors who create courses for A Cloud Guru can upload videos directly to an S3 bucket, which are immediately transcoded to a number of different formats (1080p, 720p, HLS, WebM, and so on) and are made available for students to view. The Cloud Guru platform uses Firebase as its primary client-facing database, which allows clients to receive updates in near real time without refreshing or polling (Firebase uses web sockets to push updates to all connected devices at the same time). Figure 2.2 shows a cut down version of the architecture used by A Cloud Guru.

Figure 2.2. This is a simplified version of the Cloud Guru architecture. Current production architecture has additional Lambda functions and services for performing payments, managing administration, gamification, reporting, and analytics.

Note the following about the Cloud Guru architecture given in figure 2.2:

  • The front end is built using AngularJS and is hosted by Netlify (https://netlify.com). You could use S3 and CloudFront (CloudFront is a global content delivery network provided by AWS) instead of Netlify if you wanted to.
  • Auth0 is used to provide registration and authentication facilities. It creates delegation tokens that allow the front end to directly and securely communicate with other services such as Firebase.
  • Firebase is the real-time database used by A Cloud Guru. Every client creates a connection to Firebase using web sockets and receives updates from it in near real time. This means that clients receive updates as they happen without having to poll.
  • Lecturers who create content for the platform can upload files (usually videos, but they could be other types) straight to S3 buckets via their browser. For this to work, the web application invokes a Lambda function (via the API Gateway) to request the necessary upload credentials first. As soon as credentials are retrieved, the client web application begins a file upload to S3 via HTTP. All of this happens behind the scenes and is opaque to the user.
  • Once a file is uploaded to S3, it automatically kicks off a chain of events (our event-driven pipeline) that transcodes the video, saves new files in another bucket, updates the database, and immediately makes transcoded videos available to other users. Throughout this book you’ll write a similar system and see how it works in detail.
  • To view videos, users are given permission by another Lambda function. Permissions are valid for 24 hours, after which they must be renewed. Files are accessed via CloudFront.
  • Users can submit questions and answers to the forums. Questions, answers, and comments are recorded in the database. This data is then sent for indexing to AWS CloudSearch, which is a managed searching and indexing service from AWS. This allows users to search and view questions, answers, and comments that other people have written.
Instant

Instant (http://instant.cm) is a startup that helps website owners add content management facilities—including inline text editing and localization—to their static websites. The founders, Marcel Panse and Sander Nagtegaal, describe it as instant content management system. Instant works by adding a small JavaScript library to a website and making a minor change to HTML. This allows developers and administrators to edit text elements directly via the website’s user interface. Draft edits made to the text are stored in DynamoDB (see appendix A on DynamoDB). The final, production version of the text (that the end user sees) is served as a JSON file from an S3 bucket via Amazon CloudFront (figure 2.3).

Figure 2.3. You can use Instant to add support for multiple languages, which makes it a powerful service if you need to localize your website and don’t have a content management system.

A simplified version of the Instant architecture is shown in figure 2.4. Note the following about the Instant architecture:

  • (This is not shown in the diagram.) A JavaScript library must be added to a website that wants to use Instant. Authentication is done via Google (with the user’s own Google account) by clicking a widget that appears in the website at a special URL (for example, yourwebsite.com/#edit). After successful authentication with Google, the Instant JavaScript widget authenticates with AWS Cognito, which provisions temporary AWS IAM credentials (see appendix A for information on AWS Cognito).
  • Route 53, Amazon’s Domain Name System (DNS) web service, is used to route requests either to CloudFront or to the API Gateway. (See appendix A for more information on Route 53.)
  • As a user edits text on their website, the Instant widget sends changes to the API Gateway, which invokes a Lambda function. This Lambda function saves drafts to DynamoDB, along with relevant metadata.
  • When the user decides to publish their edit (by selecting an option in the Instant widget), data from DynamoDB is read and saved in S3 as a static JSON file. This file is served from S3 via CloudFront. The Instant widget parses the JSON file received from CloudFront and updates the text on the website for the end user to see.
Figure 2.4. The Instant system uses AWS Lambda, API Gateway, DynamoDB, S3, CloudFront, and Amazon Route 53 as its main components. The system scales to support many clients.

Marcel and Sander make a few points about their system:

The use of Lambda functions leads to an architecture of microservices quite naturally. Every function is completely shielded from the rest of the code. It gets better: the same Lambda function can fire in parallel in almost infinite numbers—and this is all done completely automated.

In terms of cost, Marcel and Sander share the following:

With our serverless setup, we primarily pay for data transfer through CloudFront, a tiny bit for storage and for each millisecond that our Lambda functions run. Since we know on average what a new customer uses, we can calculate the costs per customer exactly. That’s something we couldn’t do in the past, when multiple users were shared across the same infrastructure.

Overall, Marcel and Sander find that adopting an entirely serverless approach has been a winner for them primarily from the perspectives of operations, performance, and cost.

2.2.2. Legacy API proxy

The legacy API proxy architecture is an innovative example of how serverless technologies can solve problems. As we mentioned in section 2.1.4, systems with outdated services and APIs can be difficult to use in modern environments. They might not conform to modern protocols or standards, which might make interoperability with current systems harder. One way to alleviate this problem is to use the API Gateway and Lambda in front of those legacy services. The API Gateway and Lambda functions can transform requests made by clients and invoke legacy services directly, as shown in figure 2.5.

Figure 2.5. The API proxy architecture is used to build a modern API interface over old services and APIs.

The API Gateway can transform requests (to an extent) and issue requests against other HTTP endpoints (see chapter 7). But it works only in a number of fairly basic (and limited) use cases where only JSON transformation is needed. In more complex scenarios, however, a Lambda function is needed to convert data, issue requests, and process responses. Take a Simple Object Access Protocol (SOAP) service as an example. You’d need to write a Lambda function to connect to a SOAP service and then map responses to JSON. Thankfully, there are libraries that can take care of much of the heavy lifting in a Lambda function (for example, there are SOAP clients that can be downloaded from the npm registry for this purpose; see https://www.npmjs.com/package/soap).

2.2.3. Hybrid

As we mentioned in chapter 1, serverless technologies and architectures are not an all-or-nothing proposition. They can be adopted and used alongside traditional systems. The hybrid approach may work especially well if a part of the existing infrastructure is already in AWS. We’ve also seen adoption of serverless technologies and architectures in organizations with developers initially creating standalone components (often to do additional data processing, database backups, and basic alerting) and over time integrating these components into their main systems; see figure 2.6.

Figure 2.6. The hybrid approach is useful if you have a legacy system that uses servers.

Efficient Hybrid-Serverless Job-Processing System

EPX Labs (http://epxlabs.com) proudly state that the “future of IT Operations and Application Development is less about servers and more about services.” They specialize in serverless architectures, with one of their recent solutions being a hybrid serverless system designed to carry out maintenance and management jobs on a distributed server-based infrastructure running on Amazon’s Elastic Compute Cloud (EC2) (figure 2.7).

Figure 2.7. The Hybrid-Serverless Job-Processing System designed by EPX Labs

Evan Sinicin and Prachetas Prabhu of EPX Labs describe the system they had to work with as a “multi-tenant Magento (https://magento.com) application running on multiple frontend servers. Magento requires certain processes to run on the servers such as cache clearing and maintenance operations. Additionally, all site management operations such as build, delete, and modify require a mix of on-server operations (building out directory structures, modifying configuration files, etc.) as well as database operations (creating new database, modifying data in database, and so on).” Evan and Prachetas created a scalable serverless system to assist with these tasks. Here’s how they describe how the system is built and the way it works:

  • The system is broken into two parts: the engine, which is responsible for creating, dispatching, and managing jobs, and the task processors.
  • The engine consists of several Lambda functions fronted by the Simple Notification Service (SNS—see appendix A for more information). Task processors are a mix of Lambda and Python processes.
  • A job is created by sending JSON data to the creator (part of the engine) via an SNS topic. Each job is broken down into a set of discrete tasks. Tasks fall into three categories:

    • Individual server tasks—must be executed on all servers.
    • Shared server tasks—must be executed by one server.
    • Lambda tasks—executed by a Lambda function.
  • Once created in DynamoDB, the job is sent to the scheduler, which identifies the next task to be run and dispatches it. The scheduler dispatches the task based on the type of task, either pinging a task Lambda via SNS or placing messages onto the shared or fan-out Simple Queue Service (SQS) queues (see section 2.3 for more information on these patterns).
  • Task execution on the servers is handled by custom-written Python services. Two services run on each server; one polls the shared SQS queue for shared server tasks and the other polls the individual server queue (specific to an EC2 instance). These services continually poll the SQS queues for incoming task messages and execute them based on the contained information. To keep this service stateless, all data required for processing is encapsulated in the encrypted message.
  • Each Lambda task corresponds to a discrete Lambda function fronted by an SNS topic. Typically, Lambda tasks operate on the MySQL databases backing Magento; therefore, they run in the virtual private cloud (VPC). To keep these Lambda functions stateless, all data required for processing is encapsulated in the encrypted message itself.
  • Upon completion or failure, the task processors will report the success or failure to the engine by invoking the reporter Lambda via SNS. The reporter Lambda will update the job in DynamoDB and invoke the scheduler to do any cleanup (in the case of a failure) or dispatch the next task.

2.2.4. GraphQL

GraphQL (http://graphql.org) is a popular data query language developed by Facebook in 2012 and released publicly in 2015. It was designed as an alternative to REST (Representational State Transfer) because of REST’s perceived weaknesses (multiple round-trips, over-fetching, and problems with versioning). GraphQL attempts to solve these problems by providing a hierarchical, declarative way of performing queries from a single end point (for example, api/graphql); see figure 2.8.

Figure 2.8. The GraphQL and Lambda architecture has become popular in the serverless community.

GraphQL gives power to the client. Instead of specifying the structure of the response on the server, it’s defined on the client (http://bit.ly/2aTjlh5). The client can specify what properties and relationships to return. GraphQL aggregates data from multiple sources and returns it to the client in a single round trip, which makes it an efficient system for retrieving data. According to Facebook, GraphQL serves millions of requests per second from nearly 1,000 different versions of its application.

In serverless architectures, GraphQL is usually hosted and run from a single Lambda function, which can be connected to an API Gateway (there are also hosted solutions of GraphQL like scaphold.io). GraphQL can query and write to multiple data sources, such as DynamoDB tables, and assemble a response that matches the request. A serverless GraphQL is a rather interesting approach you might want to look at next time you need to design an interface for your API and query data. Check out the following articles if you want to implement GraphQL in a serverless architecture:

2.2.5. Compute as glue

The compute-as-glue architecture shown in figure 2.9 describes the idea that we can use Lambda functions to create powerful execution pipelines and workflows. This often involves using Lambda as glue between different services, coordinating and invoking them. With this style of architecture, the focus of the developer is on the design of their pipeline, coordination, and flow of data. The parallelism of serverless compute services like Lambda helps to make these architectures appealing. The example you’re going to build in this book uses this pattern to create an event-driven pipeline that transcodes videos (chapter 3, in particular, focuses on creating pipelines and applying this pattern to solve a complex task rather easily).

Figure 2.9. The compute-as-glue architecture uses Lambda functions to connect different services and APIs to achieve a task. In this pipeline, a simple image transformation results in a new file, an update to a database, an update to a search service, and a new entry to a log service.

ListHub processing engine

EPX Labs has built a system to process large real estate XML feeds (figure 2.10). Evan Sinicin and Prachetas Prabhu say that the goal of their system is “to pull the feed, separate the large file into single XML documents, and process them in parallel. Processing includes parsing, validation, hydration, and storing.”

Figure 2.10. EPX Labs has built a system to effortlessly process large (10 GB+) XML documents.

They go on to describe how the system works in more detail:

  • The system was designed to process a real estate listing XML feed. The feed is provided by ListHub as a massive (10 GB+) XML document with millions of nested listings. This file is provided via S3 for direct download and processing. The listings conform to the Real Estate Standards Organization (RETS) standard.
  • ListHub does not have any sort of push capabilities, so the polling Lambda checks the last-modified metadata of the S3 object to see if a new feed has been posted. This usually occurs every 12 hours or so.
  • Once a new feed has been published, the polling Lambda spins up an EC2 Container Service (ECS) container to carry out the parsing of the massive file. ECS is used because this process can take a long time (Lambda can run for a maximum of 5 minutes). The ECS container has a Clojure program that asynchronously processes the feed file and places the parsed information into S3.
  • EPX Labs uses S3 as a NoSQL store. Using an S3 PutObject event trigger, each new XML listing placed into S3 triggers a Lambda that carries out the validation and hydration processes. Another S3 bucket stores processed listing IDs (as object keys). The validation Lambda can quickly verify that the listing hasn’t been processed on a previous run by checking whether the ID/key already exists.
  • The validation Lambda also triggers the hydration Lambda (“Copy Media to S3 Lambda”). This Lambda copies assets such as pictures and videos to an S3 bucket so they can be displayed on the front end.
  • The final step is to save the relevant, normalized listing data into the final data store that serves the front end and other systems. To avoid overwhelming the data store with writes, the listing data is put onto an SQS queue so it can be processed at a rate the final data store can handle.
  • Evan and Prachetas say that their approach yields a number of benefits, including that they can use S3 as a cheap, high-performance, and scalable NoSQL data store and that they can use Lambda to undertake massively concurrent processing.

2.2.6. Real-time processing

As discussed in section 2.1.3, Amazon Kinesis Streams is a technology that can help process and analyze large amounts of streaming data. This data can include logs, events, transactions, social media feeds—virtually anything you can think of—as shown in figure 2.11. It’s a good way to continuously collect data that may change over time. Lambda is a perfect tool for Kinesis Streams because it scales automatically in response to how much data there is to process.

Figure 2.11. Lambda is a perfect tool to process data in near real time.

With Kinesis Streams you can accomplish the following:

  • Control how much data is passed into a Kinesis stream before a Lambda function is invoked and how data gets to Kinesis in the first place
  • Put a Kinesis stream behind an API Gateway
  • Push data to the stream directly from a client or have a Lambda function add records to it

2.3. Patterns

Patterns are architectural solutions to problems in software design. They’re designed to address common problems found in software development. They’re also an excellent communications tool for developers working together on a solution. It’s far easier to find an answer to a problem if everyone in the room understands which patterns are applicable, how they work, their advantages, and their disadvantages. The patterns presented in this section are useful for solving design problems in serverless architectures. But these patterns aren’t exclusive to serverless. They were used in distributed systems long before serverless technologies became viable. Apart from the patterns presented in this chapter, we recommend that you become familiar with patterns relating to authentication (see chapter 4 for a discussion of the federated identity pattern), data management (CQRS, event sourcing, materialized views, sharding), and error handling (retry pattern). Learning and applying these patterns will make you a better software engineer, regardless of the platform you choose to use.

2.3.1. Command pattern

With the GraphQL architecture (section 2.2.4), we discussed the fact that a single end point can be used to cater to different requests with different data (a single GraphQL endpoint can accept any combination of fields from a client and create a response that matches the request). The same idea can be applied more generally. You can design a system in which a specific Lambda function controls and invokes other functions. You can connect it to an API Gateway or invoke it manually and pass messages to it to invoke other Lambda functions.

In software engineering, the command pattern (figure 2.12) is used to “encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations” because of the “need to issue requests to objects without knowing anything about the operation being requested or the receiver of the request” (http://bit.ly/29ZaoWt). The command pattern allows you to decouple the caller of the operation from the entity that carries out the required processing.

Figure 2.12. The command pattern is used to invoke and control functions and services from a single function.

In practice, this pattern can simplify the API Gateway implementation, because you may not want or need to create a RESTful URI for every type of request. It can also make versioning simpler. The command Lambda function could work with different versions of your clients and invoke the right Lambda function that’s needed by the client.

When to use this

This pattern is useful if you want to decouple the caller and the receiver. Having a way to pass arguments as an object, and allowing clients to be parametrized with different requests, can reduce coupling between components and help make the system more extensible. Be aware of using this approach if you need to return a response to the API Gateway. Adding another function will increase latency.

2.3.2. Messaging pattern

Messaging patterns, shown in figure 2.13, are popular in distributed systems because they allow developers to build scalable and robust systems by decoupling functions and services from direct dependence on one another and allowing storage of events/records/requests in a queue. The reliability comes from the fact that if the consuming service goes offline, messages are retained in the queue and can still be processed at a later time.

Figure 2.13. The messaging pattern, and its many variations, are popular in distributed environments.

This pattern features a message queue with a sender that can post to the queue and a receiver that can retrieve messages from the queue. In terms of implementation in AWS, you can build this pattern on top of the Simple Queue Service. Unfortunately, at the moment Lambda doesn’t integrate directly with SQS, so one approach to addressing this problem is to run a Lambda function on a schedule and let it check the queue every so often.

Depending on how the system is designed, a message queue can have a single sender/receiver or multiple senders/receivers. SQS queues typically have one receiver per queue. If you needed to have multiple consumers, a straightforward way to do it is to introduce multiple queues into the system (figure 2.14). A strategy you could apply is to combine SQS with Amazon SNS. SQS queues could subscribe to an SNS topic; pushing a message to the topic would automatically push the message to all of the subscribed queues.

Figure 2.14. Your system may have multiple queues/streams and Lambda functions to process all incoming data.

Kinesis Streams is an alternative to SQS, although it doesn’t have some features, such as dead lettering of messages (http://amzn.to/2a3HJzH). Kinesis Streams integrates with Lambda, provides an ordered sequence of records, and supports multiple consumers.

When to use this

This is a popular pattern used to handle workloads and data processing. The queue serves as a buffer, so if the consuming service crashes, data isn’t lost. It remains in the queue until the service can restart and begin processing it again. A message queue can make future changes easier, too, because there’s less coupling between functions. In an environment that has a lot of data processing, messages, and requests, try to minimize the number of functions that are directly dependent on other functions and use the messaging pattern instead.

2.3.3. Priority queue pattern

A great benefit of using a platform such as AWS and serverless architectures is that capacity planning and scalability are more of a concern for Amazon’s engineers than for you. But in some cases, you may want to control how and when messages get dealt with by your system. This is where you might need to have different queues, topics, or streams to feed messages to your functions. Your system might go one step further and have entirely different workflows for messages of different priority. Messages that need immediate attention might go through a flow that expedites the process by using more expensive services and APIs with more capacity. Messages that don’t need to be processed quickly can go through a different workflow, as shown in figure 2.15.

Figure 2.15. The priority queue pattern is an evolution of the messaging pattern.

This pattern might involve the creation and use of entirely different SNS topics, Kinesis Streams, SQS queues, Lambda functions, and even third-party services. Try to use this pattern sparingly, because additional components, dependencies, and workflows will result in more complexity.

When to use this

This pattern works when you need to have a different priority on processing of messages. Your system can implement workflows and use different services and APIs to cater to many types of needs and users (for example, paying versus nonpaying users).

2.3.4. Fan-out pattern

Fan-out is a type of messaging pattern that’s familiar to many users of AWS. Generally, the fan-out pattern is used to push a message out to all listening/subscribed clients of a particular queue or a message pipeline. In AWS, this pattern is usually implemented using SNS topics that allow multiple subscribers to be invoked when a new message is added to a topic. Take S3 as an example. When a new file is added to a bucket, S3 can invoke a single Lambda function with information about the file. But what if you need to invoke two, three, or more Lambda functions at the same time? The original function could be modified to invoke other functions (like the command pattern), but that’s a lot of work if all you need is to run functions in parallel. The answer is to use the fan-out pattern using SNS; see figure 2.16.

Figure 2.16. The fan-out pattern is useful because many AWS services (such as S3) can’t invoke more than one Lambda function when an event takes place.

SNS topics are communications/messaging channels that can have multiple publishers and subscribers (including Lambda functions). When a new message is added to a topic, it forces invocation of all subscribers in parallel, thus causing the event to fan out. Going back to the S3 example discussed earlier, instead of invoking a single--message Lambda function, you can configure S3 to push a message onto an SNS topic to invoke all subscribed functions at the same time. It’s an effective way to create event-driven architectures and perform operations in parallel. You’ll implement this yourself in chapter 3.

When to use this

This pattern is useful if you need to invoke multiple Lambda functions at the same time. An SNS topic will try and retry to invoke your Lambda functions if it fails to deliver the message or if the function fails to execute. Furthermore, the fan-out pattern can be used for more than just invocation of multiple Lambda functions. SNS topics support other subscribers such as email and SQS queues. Adding a new message to a topic can invoke Lambda functions, send an email, or push a message on to an SQS queue, all at the same time.

2.3.5. Pipes and filters pattern

The purpose of the pipes and filters pattern is to decompose a complex processing task into a series of manageable, discrete services organized in a pipeline (figure 2.17). Components designed to transform data are traditionally referred to as filters, whereas connectors that pass data from one component to the next component are referred to as pipes. Serverless architecture lends itself well to this kind of pattern. This is useful for all kinds of tasks where multiple steps are required to achieve a result.

Figure 2.17. This pattern encourages the construction of pipelines to pass and transform data from its destination (sink).

We recommend that every Lambda function be written as a granular service or a task with the single-responsibility principle in mind. Inputs and outputs should be clearly defined (that is, there should be a clear interface) and any side effects minimized. Following this advice will allow you to create functions that can be reused in pipelines and more broadly within your serverless system. You might notice that this pattern is similar to the compute-as-glue architecture we described previously. The compute-as-glue architecture is closely inspired by this pattern.

When to use this

Whenever you have a complex task, try to break it down into a series of functions (a pipeline) and apply the following rules:

  • Make sure your function follows the single-responsibility principle.
  • Make the function idempotent; that is, your function should always produce the same output for given input.
  • Clearly define an interface for the function. Make sure inputs and outputs are clearly stated.
  • Create a black box. The consumer of the function shouldn’t have to know how it works, but it must know to use it and what kind of output to expect every time.

2.4. Summary

This chapter focused on use cases, architectures, and patterns. These are critical to understand and consider before embarking on a journey to build your system. The architectures we discussing include the following:

  • Compute as back end
  • Compute as glue
  • Legacy API wrapper
  • Hybrid
  • GraphQL
  • Real-time processing

In terms of patterns, we covered these:

  • Command pattern
  • Messaging pattern
  • Priority queue pattern
  • Fan-out pattern
  • Pipes and filters pattern

Throughout the rest of this book, we’re going to apply elements we explored in this chapter, with a particular focus on creating compute-as-back-end and compute-as-glue architectures. In the next chapter, you’ll begin building your serverless applications by implementing the compute-as-glue architecture and trying the fan-out pattern.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.237.77