Implementing hexagonal architecture on AWS Lambda with Node.js

Overview

Developing evolutionary architecture with AWS Lambda

Context

Agility enables you to evolve a workload quickly, adding new features, or introducing new infrastructure as required. The key characteristics for achieving agility in a code base are loosely coupled components and strong encapsulation.

Loose coupling can help improve test coverage and create atomic refactoring. With encapsulation, you expose only what is needed to interact with a service without revealing the implementation logic.

Evolutionary architectures can help achieve agility in your design. In the book “Building Evolutionary Architectures”, this architecture is defined as one that “supports guided, incremental change across multiple dimensions”.

If you are interested to learn more about this approach, please read the blog post associated to this code example.

Project

This example provides an idea on how to implement a basic hexagonal architecture with AWS Lambda.
The folder structure represents the three key elements that characterizes the first implementation of an hexagonal architecture: ports, adapters and domain logic.

In order to run the project in your AWS account, you have to follow these steps:

  1. We need a 3rd party service to retrieve real-time currencies value for this example, you can use a service like fixer.io, Create a free account and get the API Key used for consume the API

  2. Download AWS SAM and change the API_KEY property in the template.yaml file (present in the root folder) with the Fixer.io API key

  3. Then in the adapters/CurrencyConverter, you have to replace the basepath with the URL provided by the service this line of code:

from this:

const res = await axios.get(`http://api.mysite.com?access_key=${API_KEY}&symbols=${currencies.toString()}`)

to this:

const res = await axios.get(`http://data.fixer.io/api/latest?access_key=${API_KEY}&symbols=${currencies.toString()}`)
  1. Build the project with the command sam build

  2. Deploy the project in your account sam deploy --guided

  3. Go to DynamoDB console, add an item to the stock table:

  • STOCK_ID: AMZN
  • VALUE: 3432.97

After these changes you are able to test the API retrieving the URL from the API gateway console and appending /stock/AMZN

Evolving the project

When we want to evolve the application adding a cache-aside pattern using an ElastiCache cluster for reducing the throughput towards a 3rd party service, we can do it applying some changes to the current architecture.

  1. in the ports/CurrenciesService we comment the first import and uncomment the second one. This will use a new adapter called CurrencyConverterWithCache that contains the logic for the cache-aside patter with ElastiCache Redis cluster
//const getCurrencies = require("../adapters/CurrencyConverter");
const getCurrencies = require("../adapters/CurrencyConverterWithCache");

Change the API URL in the adapters/CurrencyConverterWithCache in this way:

const res = await axios.get(`http://api.mysite.com?access_key=${API_KEY}&symbols=${currencies.toString()}`)

to this:

const res = await axios.get(`http://data.fixer.io/api/latest?access_key=${API_KEY}&symbols=${currencies.toString()}`)
  1. create a IAM Role for the Lambda, with these two policies:

IAM role policies

  1. create a ElastiCache Cluster with Redis associated to the default VPC and with the basic configuration (2 nodes with t3.micro)

  2. create a VPC endpoint for allowing the Lambda to access DynamoDB

  3. follow this tutorial for providing internet access to the Lambda. This is needed for consuming the API of the third party service in the diagram

  4. in the template.yaml file, replace the Resources parameter with the following one:

Resources:
  StocksConverterFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hexagonal-architecture/
      Handler: app.lambdaHandler
      Runtime: nodejs14.x
      MemorySize: 256
      Role: lambdavpc_role_arn_insert_here
      Environment:
        Variables:
          DB_TABLE: !Ref StocksTable
          API_KEY: API_KEY_FOR_CURRENCIES_API
          CACHE_URL: aws_elasticache_url_insert_here
          CACHE_PORT: aws_elasticache_port_insert_here
      VpcConfig:
        SecurityGroupIds:
          - sg-xxxxxx
        SubnetIds:
          - subnet-xxxxxxx
          - subnet-xxxxxxx
      Events:
        StocksConverter:
          Type: HttpApi 
          Properties:
            ApiId: !Ref StocksGateway
            Path: /stock/{StockID}
            Method: get
  StocksTable:
    Type: AWS::DynamoDB::Table
    Properties:
      AttributeDefinitions:
      - AttributeName: STOCK_ID
        AttributeType: S
      KeySchema:
      - AttributeName: STOCK_ID
        KeyType: HASH
      BillingMode: PAY_PER_REQUEST
  StocksGateway:
    Type: AWS::Serverless::HttpApi
    Properties:
      CorsConfiguration:
        AllowMethods:
          - GET
          - POST
        AllowOrigins:
          - "*"
  1. Modify the following parameters in the template.yaml file:
  • Role: insert the role name you have created in step 2
  • CACHE_URL and CACHE_PORT: add the URL and the port of the Redis cluster
  • VpcConfig: add the security group for accessing ElastiCache and the 2 subnets of your VPC

After these changes the architecture is slightly different from the basic example, thanks to hexagonal architecture we were able to atomically change an adapter and a port without changing anything else in the code base.

Contributing

Please create a new GitHub issue for any feature requests, bugs, or documentation improvements.

Where possible, please also submit a pull request for the change.

License

This library is licensed under the MIT-0 License. See the LICENSE file.

You might also like...

Alexa Skill & Google Action code that works on AWS Lambda

Alexa Skill & Google Action code that works on AWS Lambda

Jovo v4 Sample: Alexa Skill & Google Action on AWS Lambda Website - Docs - Marketplace - Template This Jovo v4 sample app showcases the following feat

Nov 22, 2022

A quickstart AWS Lambda function code generator. Downloads a template function code file, test harness file, sample SAM deffiniation and appropriate file structure.

Welcome to function-stencil 👋 A quickstart AWS Lambda function code generator. Downloads a template function code file, test harness file, sample SAM

Jun 20, 2022

A simple example repo that demonstrates the dynamic ephemeral storage solution for AWS Lambda outlined in the corresponding Storyboard Dev Blog post.

AWS Lambda Dynamic Ephemeral Storage Example A simple example repo that demonstrates the dynamic ephemeral storage solution for AWS Lambda outlined in

Jun 14, 2022

Sample code for resizing Images with Lambda@Edge using the Custom Origin. You can deploy using AWS CDK.

Sample code for resizing Images with Lambda@Edge using the Custom Origin. You can deploy using AWS CDK.

Resizing Images with Lambda@Edge using the Custom Origin You can resize the images and convert the image format by query parameters. This Lambda@Edge

Dec 11, 2022

🚀 Using top-level await in AWS Lambda with TypeScript, esbuild and Serverless Framework

🚀 Using top-level await in AWS Lambda with TypeScript, esbuild and Serverless Framework

🚀 Top-level await in AWS Lamba with TypeScript Articles https://dev.to/oieduardorabelo/top-level-await-in-aws-lamba-with-typescript-1bf0 https://medi

Nov 23, 2022

A technology stack solution using the AWS Serverless architecture.Atlas stack for building applications focused on generating value.

Atlas A technology stack solution using the AWS Serverless architecture.Atlas stack for building applications focused on generating value. Description

Dec 15, 2022

Get-A-Room example application using Domain Driven Design and Clean Architecture. Written in TypeScript and deployed to AWS with a serverless stack.

Get-A-Room example application using Domain Driven Design and Clean Architecture. Written in TypeScript and deployed to AWS with a serverless stack.

Domain Driven Microservices on AWS in Practice This project provides a Domain Driven Design & Clean Architecture-informed, multi-service event-driven

Dec 31, 2022

Deploy an Architect project from GitHub Actions with keys gathered from aws-actions/configure-aws-credentials

Deploy an Architect project from GitHub Actions with keys gathered from a specific AWS IAM Role federated by an IAM OIDCProvider. CloudFormation to cr

Apr 6, 2022

a stack-separated way to bringing together common AWS services useful in a fullstack application that uses AWS Amplify libraries

Fullstack CDK Helpers This project helps developers create common AWS services that are useful in creating fullstack applications. Backend services ar

Nov 26, 2022
Comments
  • Ports are not abstracting adaptors

    Ports are not abstracting adaptors

    I used internally this sample for training with my team, and they got confused by the responsibility of ports. They struggled to understand the abstraction and to translate it to typescript. This issue is an attempt to make things more clear for developers new to hexagonal architecture.

    I believe ports should enforce a contract between Domain logic and Adaptors, and protect changes to happen on domain logic when the implementation details present in Adaptors change.

    Here the port is not only enforcing the contract but tied to the adaptor on itself.

    for example, the Repository port is tied to the implementation of a DynamoDB table with a defined schema.

    https://github.com/aws-samples/aws-lambda-hexagonal-architecture/blob/1e9ec9a4e05479533ac01018adadf8e544b3a156/hexagonal-architecture/ports/Repository.js#L6

    If we happen to change the database schema (changing key names, moving to a single table design, etc..), the port is not protecting the Domain logic from the implementation detail in the Adaptor. Moving to a single table design will imply changing Adaptor/ports and domain logic.

    If we were in a typed language, the port would be represented by an Interface, that defines the contract (Input/Output), and the adaptor would implement this interface (the contract).

    opened by jeremycare 0
Owner
AWS Samples
AWS Samples
MerLoc is a live AWS Lambda function development and debugging tool. MerLoc allows you to run AWS Lambda functions on your local while they are still part of a flow in the AWS cloud remote.

MerLoc MerLoc is a live AWS Lambda function development and debugging tool. MerLoc allows you to run AWS Lambda functions on your local while they are

Thundra 165 Dec 21, 2022
AWS Lambda & Serverless - Developer Guide with Hands-on Labs. Develop thousands line of aws lambda functions interact to aws serverless services with real-world hands-on labs

AWS Lambda & Serverless - Developer Guide with Hands-on Labs UDEMY COURSE WITH DISCOUNTED - Step by Step Development of this Repository -> https://www

awsrun 35 Dec 17, 2022
Under the Sea is an official AWS workshop delivered by AWS SAs and AWS Partners to help customers and partners to learn about AIOps with serverless architectures on AWS.

Under the Sea - AIOps with Serverless Workshop Under the Sea is an exciting MMORPG developed by the famous entrepreneur behind Wild Rydes, the most po

AWS Samples 4 Nov 16, 2022
Everynode allows you to run any version of Node.js in AWS Lambda, in any commercial AWS region

Run Any Node.js Version in AWS Lambda Everynode allows you to run any version of Node.js in AWS Lambda, in any commercial AWS region. We add support f

Fusebit 116 Dec 15, 2022
A serverless AWS expense tracker API. AWS Lambda functions, API gateway, and Dynamodb are among the ingredients.

AWS-Serverless-API A serverless AWS expense tracker API. AWS Lambda functions API gateway Dynamodb Endpoints Create a new expense: Method: POST Body f

Ondiek Elijah Ochieng 1 Jul 16, 2022
A monorepo that uses the AWS Cloud Development Kit to deploy and configure nanomdm on AWS lambda.

NanoMDM on AWS This repo builds and configures a nanomdm server to run on AWS lambda. It uses the Cloud Development Kit and tries to follow best pract

Stevie Clifton 4 May 26, 2022
Lumos is an AWS Lambda visualizer and open source alternative to AWS CloudWatch.

Lumos Lambda Metrics Visualizer Table of Contents About Lumos Techologies Used Getting Started Key Lambda Metrics How to Contribute License Contributo

OSLabs Beta 36 Nov 5, 2022
Learn Web 2.0 and Web 3.0 Development using Next.js, Typescript, AWS CDK, AWS Serverless, Ethereum and AWS Aurora Serverless

Learn Web 2.0 Cloud and Web 3.0 Development in Baby Steps In this course repo we will learn Web 2.0 cloud development using the latest state of the ar

Panacloud Multi-Cloud Internet-Scale Modern Global Apps 89 Jan 3, 2023
AWS Lambda and API Gateway, simplified for Javascript

alanajs AWS Lambda and API Gateway, simplified for JavaScript About alanajs Make setting up Lambda microservices easier than ever. alanajs is a free,

OSLabs Beta 64 Aug 1, 2022