Serverless Patterns
Tags: serverless, alexa, iot
Published: 2020-04-10 05:48:26


Serverless sets free developers from the burden of managing infrastructures. When we go serverless, we do not have to worry anymore about machines, containers and, in general, traditional devops or sysadmin. The benefits are lower costs for operations and fewer headaches.

However, new problems arise. One of the most important ones is that Serverless is not configuration-less. As networks grow (and they do very soon), configuration file size explodes. Try to imagine the pain to manage and understand a network like this (from [1]).

![](https://theburningmonk.com/wp-content/uploads/2016/12/overall-1024x889.png)

We have a lot of tools that help us in building and deploying Serverless network, but working with large networks is still hard, error prone and time consuming.

To put it with the words [3] of Tim Wagner, AWS Lambda inventor
> Configuration & AWS CloudFormation are still creating friction [...] 
Whatever your position on recent approaches [...] it’s clear that CloudFormation and other vendor-provided options still aren’t nailing it.

When we solve complex problems, we usually split them into simpler ones. Can we use the "divide et impera" principle to tame the complexity of Serverless networks? Is it possible to decompose large networks into smaller ones? Can we build networks from reusable more manageable units?

In a recent paper, Davide Taibi and colleagues have identified some common patterns for serverless networks used in industry [2].

For example, this simple network is a Polling Event Processor (PEP) from [2]. It describes an architectural component for simulating real time updates when an external system does not support them. The basic idea is the following. A clock periodically triggers a Lambda whose role is to check if there are updates. In that case, a notification with the new state is sent to another Lambda.

![](https://dev-to-uploads.s3.amazonaws.com/i/nyzttui4k9s9p6moemjb.png)

We can imagine that the PEP  is a Lego brick, aka the unit of composition. As a Lego brick, a PEP can be used in different contexts and more than once.

If we reason in terms of network patterns instead of individual links between lambdas and resources, as opposed to what most tools do, a complex Serverless network can be seen as a composition of simpler networks. I believe that, in this way, working with large network wouldn't be that hard.

Here, following Taibi's approach, I would like to talk about some other patterns I met while working with Alexa Home Skills. Then, I'll show how we can reuse patterns over different network configurations using a simple composition mechanism.

## A case study: Serverless patterns for Alexa

Let's introduce how Alexa works. When you utter a voice command, your utterance is interpreted by Amazon cloud services. Then, a structured event is sent to a custom AWS Lambda. In this Lambda, you implement the logic corresponding to the voice command. That's it.

There are two kinds of responses you can send back to Alexa from your lambda: a `Response` or a `DeferredResponse`. Here, we consider only the latter one because it allows us to build an interesting scenario.

A `DeferredResponse` is equivalent to an HTTP `202` response. We tell Alexa that we got the event, but we are not ready to return a response. We will notify Alexa (within 6 seconds) in an asynchronous way. More precisely, `AlexaResponses` are sent to an `AlexaGateway`, represented by the red gate in the picture below.

Typically, when you implement the custom logic for a Lambda, you interface with existing 3rd party APIs. For example, consider this simple use case.

![](https://dev-to-uploads.s3.amazonaws.com/i/e9n0aabmvhs725ig4tzt.png)

Here, we want to map Alexa events to Http requests for the APIs of a bulb's manufacturer. So, when we receive a `TurnOn` directive, we will switch on lights.

By definition, we do not control 3rd party APIs. Hence, we need to adapt our infrastructure to different technical needs.

Here, we consider two kinds of APIs: sync and async. Sync APIs contain an updated state in their HTTP responses, e.g. when we send a "turn on" command to the APIs, we get back a response with the new state of the bulb (e.g. on) or an error. Async APIs, instead, respond with a `202` status code, i.e. the command has been received but the system state is not updated synchronously. Besides, we can have even more complex scenarios: some APIs have push notification mechanisms, others don't.

It is clear that we need to make different infrastructure choices if APIs are sync or async, if they have push notifications or not and so on. On the top of that, we would like to reuse as much infrastructure as we can for different projects.

## Functional patterns

A functional pattern describes a function or role of a subnetwork in the context of a larger network. Which are the functional patterns for Alexa? Let's see.

### The entrypoint

The `entrypoint` is a simple component whose aim is to accept Alexa events. It acts like a `router` from [2] in the sense that it "distribute[s] the execution based on payload".

* If the Alexa event is a `AcceptGrantDirective`, then it returns an `AcceptGrantResponse`, synchronously. We will see what it means later.
* If it is a `ReportState`, it returns a `ReportState` (i.e. the current state of a device), synchronously.
* If it is a `Discover`, it returns a `DiscoverEvent` (i.e. a list of available devices), synchronously.
* Otherwise, it returns a `DeferredResponse` and forwards the event to a queue in such a way that it can be processed by another lambda asynchronously.

![](https://dev-to-uploads.s3.amazonaws.com/i/8akuqqhznk73d4em2gdw.png)

### The sync processor

The `sync-processor` takes an event from the queue of deferred events and builds an HTTP request for sync APIs. Since APIs are sync, an HTTP response contains the system's updated state and so the `sync-processor` can also build a proper `AlexaResponse`. The `AlexaResponse` will be sent over to the dispatcher. In other words, a `sync-processor` is a combination of `proxy`, `fifo` and `router` patterns described in [2].

![](https://dev-to-uploads.s3.amazonaws.com/i/8ycjrgnlvdgrln0so0w8.png)

### The async processor

The `async-processor` takes an event from the queue of deferred events and builds HTTP requests for async APIs. However, it cannot build an `AlexaResponse` because the HTTP response from the APIs is only a `202` response. We need to store the information that we are waiting for an async response from the APIs. So, the `async-processor` will store this information (i.e. Alexa event, HTTP requests and status) in a database. In this way, when the APIs notify the system of a state update, another Lambda will be able to build a proper `AlexaResponse` to be sent over. I do not see this pattern in [2], but it is a common pattern in distributed systems.

![](https://dev-to-uploads.s3.amazonaws.com/i/qt3wz8e6tc7czzs49prr.png)

### The dispatcher

When a `DeferredResponse` is sent back to Alexa, we have 6 seconds to return an `AlexaResponse`, asynchronously. Asynchronous responses are sent to the `AlexaGateway`.

So, when a `sync-processor` or an `async-processor` have built an `AlexaResponse`, the response should be sent to the `AlexaGateway`. However, it is not that simple. We need to be authorized by the Alexa user.

Explaining the details about how it works is out of the scope of this post. If you are interested, you can read Alexa documentation.

In a few words, the user must have already authorized push notifications sending an `AcceptGrantDirective`. We need to keep the grant code somewhere (here, for simplicity's sake, we will store it into a table). Then, when we need to send an `AlexaResponse` to the `AlexaGateway`, we have to obtain an access token using the grant code we stored previously. Then, we can send the `AlexaResponse` with the access token to the `AlexaGateway`.

Does it sound complicated? Not that much. Fortunately, this is the job for the `dispatcher` pattern.

![](https://dev-to-uploads.s3.amazonaws.com/i/uht2u0pnvrskqpe1yxwm.png)

### Polling Event Processor (from [2])

If APIs have push notifications, then we need only an http gateway that invokes the second Lambda in the `async-processor` components. In this way, an `AlexaResponse` is built and sent over, when APIs notify a state update.

If APIs don't have push notifications, we need another component. The Polling Event Processor defined in [2] is exactly what we should use.

![](https://dev-to-uploads.s3.amazonaws.com/i/nyzttui4k9s9p6moemjb.png)

### Composing sub-networks

Now we have some reusable patterns. Let's see how we can glue them together in order to implement different use cases. 

The composition mechanism is pretty simple: you compose two networks overlapping entities with the same names/colors. In this way, from the patterns we defined above, we can define different network configurations reusing the same components and code.

First, if APIs are synchronous, we can use the `sync-processor` composed with the `entrypoint` and the `dispatcher`.

![](https://dev-to-uploads.s3.amazonaws.com/i/xe2ub1ujaa1vxxderdg4.png)

Instead, if APIs are asynchronous, we need the `async-processor`. In this version, we assume that APIs have push notifications, so we are updated about new state changes using an API gateway.

![](https://dev-to-uploads.s3.amazonaws.com/i/7tk1465q38s561nps2t2.png)

In the last example, APIs are asynchronous, but they do not have push notifications. As discussed above, we can simulate real time updates using polling.

![](https://dev-to-uploads.s3.amazonaws.com/i/n43olr6vmbhwphzfggu8.png)

In an upcoming post, we will describe the composition mechanism more in detail. However, without going too deep into the technicalities, you can realize how simple it is. As an exercise, you can imagine more network configurations reusing the same components and implementing new ones.

Some suggestions:

1. An `optimistic-processor` is a processor for Async APIs that acts like a `sync-processor` assuming that the Async APIs will update the state as we expect.
2. What are the possible concerns with simulating push notifications using polling in a Serverless context? Can you imagine a better solution?
3. What if we store access grants on a local hub within users' premises?

## Conclusions

Building networks is a well-known problem in Serverless computing. Here, we show how we can build networks from smaller ones. In this way, we can share and reuse architectural components more easily.

## Acknowledgements

Thanks to Purepoint team for criticisms and comments. This material is a re-adaptation of an internal tech talk I gave a few weeks ago. Besides, if you want to check it out, I have an incomplete WIP [PoC](https://github.com/AscentSoftware/Wires) where I tried to explain these ideas with code.

## Credits

Diagrams made with Drawio.

The idea of overlapping graphs comes from [Algebraic Graphs](https://github.com/algebraic-graphs).

## References

[1] Yan Cui [Yubls Road to Serverless Architecture](https://theburningmonk.com/2016/12/yubls-road-to-serverless-architecture-part-1) 2016
[2] Davide Taibi [Serverless Patterns](https://medium.com/@taibi.davide/serverless-patterns-e1fb3f1d753e) 2020 (via [off-by-none](https://www.jeremydaly.com/newsletter/))
[3] Tim Wagner [The State of Serverless, circa 2019](https://read.acloud.guru/the-state-of-serverless-circa-10-2019-2bfd0e605700) 2019