Chaining Serverless Functions for Stateful Workflows: AWS Step Functions using Adapter Pattern

exportNote still takes 15 seconds to respond.

What we need is to return the response immediately and continue executing other tasks sequentially.

So what is the workflow here:fetch notes — — → invoke translate — — → invoke sendMailThere are more than one way to coordinate this workflow.

We can invoke lambdas asynchronously, use SNS, SQS, implement our own error handling, retry logic in the code…Or, we can simply use AWS Step Functions.

AWS Step Functions provides serverless orchestration for modern applications.

Orchestration centrally manages a workflow by breaking it into multiple steps, adding flow logic, and tracking the inputs and outputs between the steps.

In a nutshell, Step Functions allows us to define our workflows as state machines.

We will just start this state machine and Step Functions will take care of the rest.

I want to leave exportNotes lambda outside of the Step Functions definition, since it is the entry point to the API request and knows about the business logic.

I want independent pieces to be in this state machine, so that we can reuse it for other workflows as well.

translate and sendMail state machineWe can define this state machine in Amazon States Language:translateEmailSFN ASLYou can create Step Functions using the JSON definition above.

I created one and named it translateEmailSFN .

Now we can invoke translateEmailSFN in exportNotes, and pass all user notes as input.

What happens then?Step Functions starts by invoking translate Lambda function with given input and waits for it to finish.

Then, it invokes sendMail Lambda function with the output of previous task (output of translate) and waits for it to finish.

Can you see the problem here?We are giving the output of translate function as input to sendMail .

They have their own interface and do not understand each other.

This is the output of translate function: {"text": "Translated text"}This is the input format sendMail function accepts: {"to": ".

", "subject": "Notes", "message": "Translated text"}Missing piece: AdaptersWe have discrete Lambda functions that performs one work.

That is good but we still need a way to connect them together in our workflow.

We don’t want to change their input/output structure and make them compatible with each other.

This is not scalable and creates dependencies between them.

We want to make them work together, without modifying their source code.

This is where adapters come into play.

They are regular Lambda functions which prepares the input for next task, using the data in the flow.

We can add an adapter between these two incompatible states.

translate and sendMail state machine with adapterHere is the new Step Functions definition for the state machine above:Notice the adapter Lambda between translate and sendMail .

It is basically a bridge between them.

It prepares input for sendMail by using the output of translate.

We also introduced InputPath and ResultPath to the definition, to give the data a nice structure.

We have 4 Lambda functions: exportNote translate adapter sendMail and one Step Function definition translateEmailSFN .

Let’s write the final versions of the Lambda functions, translate and sendMail won’t change.

exportNotes : Fetch all the notes of user, prepare input data and invoke translateEmailSFN , return the response without waiting for Step Functions response.

exportNotes handlertranslate Lambda function:translate handlersendMail Lambda function:sendMail handleradapter Lambda function:That’s it!We can trace the data in translateEmailSFN to understand how independent pieces works together:Now we can easily add more Lambda functions to this workflow using adapters.

But remember, every adapter you use means one more lambda function invocation, which can be unnecessary overhead if you overuse them.


. More details

Leave a Reply