O blog da AWS

Melhores práticas para trabalhar com a linguagem de modelo Apache Velocity no Amazon API Gateway

Por Ben Freiberg e Markus Ziller
Este post foi escrito por Ben Freiberg, arquiteto de soluções sênior, Markus Ziller, arquiteto de soluções sênior e adaptado para Português por Daniel ABIB, arquiteto sênior de soluções para Ente r prise.
Um dos padrões Serverless mais comuns são as APIs criadas com o Amazon API Gateway e o AWS Lambda. Tal abordagem é suportada por muitas estruturas diferentes em várias linguagens. No entanto, a integração direta com a AWS pode permitir que os clientes aumentem a eficiência de custos e a resiliência de sua arquitetura Serverless. Este blog post discute as melhores práticas para utilizar o Apache Velocity Templates na integração direta de serviços no API Gateway.

Decidindo entre a integração por meio de modelos Velocity e funções Lambda

Muitos casos de uso de modelos do Velocity no API Gateway também podem ser resolvidos com o Lambda. Com o Lambda, a complexidade da integração com diferentes back-ends é transferida da linguagem de modelagem Velocity (VTL) para a linguagem de programação. Isto permite que os clientes usem estruturas e metodologias existentes do ecossistema de sua linguagem de programação preferida.

Entretanto, muitos clientes optam pela tecnologia Serverless na AWS para criar arquiteturas enxutas e o uso de serviços adicionais, como funções Lambda, podem adicionar complexidade ao seu aplicativo. Há diferentes considerações em que os clientes podem usar para avaliar as vantagens e desvantagens entre as duas abordagens.

Experiência do desenvolvedor

O Apache Velocity tem vários operadores que podem ser usados quando uma expressão é avaliada, principalmente nas diretivas #if e #set. Esses operadores permitem que você implemente transformações complexas e lógica de negócios em seus modelos do Velocity.

No entanto, isso adiciona complexidade a vários aspectos do fluxo de trabalho de desenvolvimento:

  • Teste: testar modelos do Velocity é possível, mas as ferramentas e metodologias são menos maduras do que as linguagens de programação tradicionais usadas nas funções do Lambda.
  • Bibliotecas: o API Gateway oferece funções utilitárias para VTL que simplificam casos de uso comuns, como transformação de dados. Outras funcionalidades normalmente oferecidas pelas bibliotecas de linguagens de programação (por exemplo, a Python Standard Library) podem não estar disponíveis em seu modelo.
  • Registro em log: não é possível registrar informações no Amazon CloudWatch a partir de um modelo do Velocity, portanto, não há opção de reter essas informações.
  • Rastreamento: o API Gateway oferece suporte ao rastreamento de solicitações via AWS X-Ray para integrações nativas com serviços como o Amazon DynamoDB.

Você deve usar a VTL para mapeamento e transformações de dados, em vez de lógica comercial complexa. Há exceções, mas as desvantagens de usar a VTL para outros casos de uso geralmente superam os benefícios.

Ciclo de vida da API

O ciclo de vida da API é um aspecto importante a ser considerado ao decidir sobre o Velocity ou o Lambda. Nos estágios iniciais, os requisitos geralmente não estão bem definidos e podem mudar rapidamente ao explorar o espaço da solução. Isso geralmente acontece ao se integrar a bancos de dados como o Amazon DynamoDB e descobrir a melhor maneira de organizar os dados na camada de persistência.

Para o DynamoDB, isso geralmente significa alterações em atributos, tipos de dados ou chaves primárias. Nesses casos, é uma decisão sensata começar com o Lambda. Escrever código em uma linguagem de programação pode dar aos desenvolvedores mais liberdade e flexibilidade ao incorporar mudanças. Isto reduz o ciclo de feedback das mudanças e pode melhorar a experiência do desenvolvedor.

Quando uma API amadurece e é executada na produção, as mudanças geralmente se tornam menos frequentes e a estabilidade aumenta. Nesse ponto, pode fazer sentido avaliar se a função Lambda pode ser substituída movendo a lógica para os modelos do Velocity. Especialmente para APIs ocupadas, o esforço único de mover a lógica do Lambda para os modelos do Velocity pode valer a pena a longo prazo, pois elimina o custo das invocações do Lambda.

Latência

Em aplicativos da web, um fator importante do desempenho percebido pelo usuário é o tempo necessário para carregar uma página. Em aplicativos modernos (SPA – Single Page Application), isto geralmente significa várias solicitações para APIs de back-end. O API Gateway oferece recursos para minimizar a latência das chamadas na camada de API. Com o Lambda para integração de serviços, um componente adicional é adicionado ao fluxo de execução da solicitação, o que inevitavelmente introduz latência adicional.

O grau dessa latência adicional depende das especificidades da carga de trabalho e geralmente é tão baixo quanto alguns milissegundos.

O exemplo a seguir não mede nenhuma diferença significativa na latência além de inícios a frio dos ambientes de execução de uma API CRUD básica com uma função Lambda Node.js que consulta o DynamoDB. Eu observo resultados semelhantes para Go e Python.

Concorrência e escalabilidade

A simultaneidade e a escalabilidade de uma API mudam quando há uma função Lambda adicional no caminho de execução da solicitação. Devendo-se a isto às diferentes cotas de serviço e aos comportamentos gerais de escalabilidade em diferentes serviços.

Para o API Gateway, a cota padrão atual é de 10.000 solicitações por segundo (RPS) com uma capacidade de intermitência adicional fornecida pelo algoritmo de token bucket, usando uma capacidade máxima de 5.000 solicitações. As cotas do API Gateway são independentes da região, enquanto os limites de simultaneidade padrão do Lambda dependem da região.

Após a explosão inicial, a simultaneidade de suas funções pode aumentar em mais 500 instâncias a cada minuto. Isso continua até que haja instâncias suficientes para atender a todas as solicitações ou até que um limite de simultaneidade seja atingido. Para obter mais detalhes sobre esse tópico, consulte Entendendo a escalabilidade e a taxa de transferência do AWS Lambda.

Se sua carga de trabalho apresentar picos acentuados de tráfego, uma integração direta com sua camada de persistência pode levar a uma melhor capacidade de lidar com esses picos sem limitar as solicitações dos usuários. Especialmente para regiões com uma capacidade de pico inicial de 1000 ou 500, isso pode ajudar a evitar a limitação e fornecer uma experiência de usuário mais consistente.

Melhores práticas

Organize seu projeto para suporte de ferramentas

Quando a VTL é usada em artefatos de infraestrutura como código (IaC), como modelos do AWS CloudFormation, ela deve ser incorporada ao documento do IaC como uma string.

Essa abordagem tem três desvantagens principais:

  • Especialmente com modelos Velocity de várias linhas, levando-se a definições de IaC que são difíceis de ler ou escrever.
  • Ferramentas como IDEs ou Linters não funcionam com representações de seqüências de caracteres dos modelos do Velocity.
  • Os modelos não podem ser facilmente usados fora da definição do IaC, como para testes locais.

Cada aspecto afeta a produtividade do desenvolvedor e torna a implementação mais propensa a erros.

Você deve dissociar a definição dos modelos Velocity da definição dos modelos de IaC sempre que possível. Para o CDK, a implementação requer apenas algumas linhas de código.

// The following code is licensed under MIT-0 
import { readFileSync } from 'fs';
import * as path from 'path';

const getUserIntegrationWithVTL = new AwsIntegration({
      service: 'dynamodb',
      integrationHttpMethod: HttpMethods.POST,
      action: 'GetItem',
      options: {
        // Omitted for brevity
        requestTemplates: {
          'application/json': readFileSync(path.join('path', 'to', 'vtl', 'request.vm'), 'utf8').toString(),
        },
        integrationResponses: [
          {
            statusCode: '200',
            responseParameters: {
              'method.response.header.access-control-allow-origin': "'*'",
            },
            responseTemplates: {
              'application/json': readFileSync(path.join('path', 'to', 'vtl', 'request.vm'), 'utf8').toString(),
            },
          },
        ],
      },
    });
JavaScript

Outra vantagem dessa abordagem é que ela o forcará a externalizar variáveis em seus modelos. Ao definir modelos do Velocity dentro dos documentos do IaC, é possível fazer referência a outros recursos no mesmo documento do IaC e definir esse valor no modelo do Velocity por meio da concatenação de strings. No entanto, isso codifica o valor no modelo, em oposição à forma recomendada de usar variáveis de estágio.

Teste os modelos do Velocity localmente

Um desafio frequente que os clientes enfrentam com os modelos do Velocity é como encurtar o ciclo de feedback ao implementar um modelo. Um fluxo de trabalho comum para testar alterações nos modelos é:

  1. Recupere o contexto da solicitação: o API Gateway recupera os parâmetros da solicitação e as variáveis de estágio.
  2. Make request: body: o API Gateway usa o modelo e as variáveis de 1 para renderizar um documento JSON.
  3. Enviar solicitação: o API Gateway faz uma chamada de API para o respectivo serviço da AWS. Ele abstrai a autorização (por meio de sua função do IAM), a codificação e outros aspectos da solicitação, de modo que somente o corpo da solicitação precise ser fornecido pelo API Gateway
  4. Recuperar resposta: o API Gateway recupera uma resposta JSON da chamada da API.
  5. Crie o corpo da resposta: se a chamada for bem-sucedida, a resposta JSON será usada como entrada para renderizar o modelo de resposta. O resultado será então enviado de volta ao cliente que iniciou a solicitação ao API Gateway.

Dependendo da duração da implantação da pilha, geralmente, poderá levar a ciclos de feedback de vários minutos. Embora o ecossistema de testes do Velocity esteja longe de ser tão extenso quanto o das linguagens de programação convencionais, ainda há maneiras de melhorar a experiência do desenvolvedor ao escrever VTL.

Mecanismo de renderização local Velocity com o AWS SDK

Quando o API Gateway recebe uma solicitação que tem um destino de integração com a AWS, as seguintes etapas acontecem:

  1. Retrieve request context: API Gateway retrieves request parameters and stage variables.
  2. Make request: body:  API Gateway uses the template and variables from 1 to render a JSON document.
  3. Send request: API Gateway makes an API call to the respective AWS Service. It abstracts Authorization (via it’s IAM Role), Encoding and other aspects of the request away so that only the request body needs to be provided by API Gateway
  4. Retrieve response: API Gateway retrieves a JSON response from the API call.
  5. Make response body: If the call was successful the JSON response is used as input to render the response template. The result will then be sent back to the client that initiated the request to the API Gateway

Para simplificar nosso fluxo de trabalho de desenvolvimento, você pode replicar localmente o fluxo acima com o AWS SDK e um mecanismo de renderização Velocity de sua escolha.

Eu recomendo usar o Node.js por dois motivos:

  • A biblioteca velocity.js é um mecanismo de renderização Velocity leve, mas poderoso.
  • Os métodos do cliente (por exemplo, DynamoDBClient.query (JSONBody)) do AWS SDK para JavaScript geralmente esperam o mesmo corpo JSON que a API REST da AWS. Para a maioria dos casos de uso, nenhuma transformação (por exemplo, caso camel para caso Pascal) é necessária

O trecho a seguir mostra como testar os modelos do Velocity para solicitação e resposta de uma integração de serviços do DynamoDB. Ele carrega modelos de arquivos e os renderiza com contexto e parâmetros.

// The following code is licensed under MIT-0 
const fs = require('fs')
const Velocity = require('velocityjs');
const AWS = require('@aws-sdk/client-dynamodb');
const ddb = new AWS.DynamoDB()

const requestTemplate = fs.readFileSync('path/to/vtl/request.vm', 'utf8')
const responseTemplate = fs.readFileSync(''path/to/vtl/response.vm', 'utf8')

async function testDynamoDbIntegration() {
  const requestTemplateAsString = Velocity.render(requestTemplate, {
    // Mocks the variables provided by API Gateway
    context: {
      arguments: {
        tableName: 'MyTable'
      }
    },
    input: {
      params: function() {
        return 'someId123'
      },
    },
  });

  print(requestTemplateAsString)

  const sdkJsonRequestBody = JSON.parse(requestTemplateAsString)
  const item = await ddb.query(sdkJsonRequestBody)

  const response = Velocity.render(responseTemplate, {
    input: {
      path: function() {
        return {
          Items: item.Items
        }
      },
    },
  })

  const jsonResponse = JSON.parse(response)
}
JavaScript

Essa abordagem não abrange todos os casos de uso e, em última análise, deve ser validada por meio da implantação do modelo. Contudo, isso ajuda a reduzir a duração de um ciclo de feedback de minutos para alguns segundos e permite interações mais rápidas no desenvolvimento de modelos do Velocity.

Conclusão

Esta postagem do blog discute considerações e melhores práticas para trabalhar com Velocity Templates no API Gateway. A experiência do desenvolvedor, a latência, o ciclo de vida da API, o custo e a escalabilidade são fatores-chave na escolha entre Lambda e VTL. Para a maioria dos casos de uso, recomendamos o Lambda como ponto de partida e o VTL como etapa de otimização.

Configurar um ambiente de teste local para a VTL ajuda a reduzir significativamente o ciclo de feedback e a aumentar a produtividade do desenvolvedor. O AWS CDK é a estrutura de IaC recomendada para trabalhar com projetos de VTL, pois permite que você organize com eficiência sua infraestrutura como projeto de código para suporte de ferramentas.

Para obter mais recursos de aprendizado Serverless, visite Serverless Land.

 

Este artigo foi traduzido do Blog da AWS em Inglês.

 


Sobre o autor

Ben Freiberg é Senior Solutions Architect

 

 

 

 

Markus Ziller é Senior Solutions Architect

 

 

 

 

Tradutor

Daniel Abib é Enterprise Solution Architect na AWS, com mais de 25 anos trabalhando com gerenciamento de projetos, arquiteturas de soluções escaláveis, desenvolvimento de sistemas e CI/CD, microsserviços, arquitetura Serverless & Containers e segurança. Ele trabalha apoiando clientes corporativos, ajudando-os em sua jornada para a nuvem.

https://www.linkedin.com/in/danielabib/