Git Product home page Git Product logo

aws-lambda-hexagonal-architecture's Introduction

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.

aws-lambda-hexagonal-architecture's People

Contributors

amazon-auto avatar lucamezzalira avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.