Voltar ao blog

Serverless na AWS: Quando Vale a Pena e Quando é Cilada

Minha experiência real com Lambda, API Gateway e DynamoDB. Onde serverless brilha, onde complica, e quanto realmente custa em produção.

RS
Richard Sakaguchi Solution Architect

Todo mundo fala que serverless é o futuro. Depois de usar em produção por um bom tempo, posso dizer: depende. Tem cenário que é perfeito, tem cenário que vira pesadelo.

Onde Serverless Brilha

Vou direto ao ponto - serverless faz sentido quando:

  • Tráfego imprevisível - Escala de 0 a milhares sem você fazer nada
  • Workloads esporádicos - Paga só quando executa
  • Eventos e integrações - Reagir a upload S3, mensagem SQS, etc
  • APIs simples - CRUD sem muita lógica complexa

Já usei pra processar webhooks de pagamento. Tráfego zero a maior parte do tempo, picos quando tinha campanha. Custo total: menos de $5/mês.

Onde Vira Dor de Cabeça

Por outro lado, evito serverless quando:

  • Processamento longo - Lambda tem limite de 15 minutos
  • Conexões persistentes - WebSocket funciona, mas é gambiarrento
  • Tráfego alto e constante - EC2/Fargate fica mais barato
  • Debug complexo - Rastrear problema em 50 Lambdas encadeados não é divertido

Como Estruturo um Projeto Serverless

Uso o Serverless Framework porque simplifica muito o deploy. A estrutura que funciona pra mim:

project/
├── src/
│   ├── handlers/          # Cada Lambda é um arquivo
│   │   ├── createItem.ts
│   │   ├── getItem.ts
│   │   └── listItems.ts
│   ├── services/          # Lógica de negócio
│   └── utils/             # Helpers
├── serverless.yml
└── package.json

O serverless.yml básico:

service: minha-api

provider:
  name: aws
  runtime: nodejs20.x
  region: sa-east-1

  environment:
    TABLE_NAME: ${self:service}-${sls:stage}

functions:
  createItem:
    handler: src/handlers/createItem.handler
    events:
      - http:
          path: /items
          method: post
          cors: true

  getItem:
    handler: src/handlers/getItem.handler
    events:
      - http:
          path: /items/{id}
          method: get
          cors: true

O Padrão de Handler que Uso

Depois de muito refatorar, cheguei nessa estrutura que deixa o código testável:

// src/handlers/createItem.ts
import { APIGatewayProxyHandler } from 'aws-lambda';
import { ItemService } from '../services/ItemService';

const itemService = new ItemService();

export const handler: APIGatewayProxyHandler = async (event) => {
  try {
    const body = JSON.parse(event.body || '{}');

    // Validação simples - não precisa de lib pesada
    if (!body.name) {
      return {
        statusCode: 400,
        body: JSON.stringify({ error: 'name é obrigatório' }),
      };
    }

    const item = await itemService.create(body);

    return {
      statusCode: 201,
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(item),
    };
  } catch (error) {
    console.error('Erro ao criar item:', error);

    return {
      statusCode: 500,
      body: JSON.stringify({ error: 'Erro interno' }),
    };
  }
};

A lógica fica no service, separada do handler. Assim consigo testar sem precisar mockar o evento inteiro do API Gateway.

DynamoDB - Single Table Design

DynamoDB é diferente de SQL. No começo eu criava uma tabela por entidade, como faria no MySQL. Erro.

O lance é usar Single Table Design - tudo na mesma tabela, diferenciando pelo padrão da chave:

PK              | SK              | Dados
----------------|-----------------|------------------
USER#123        | PROFILE         | nome, email, ...
USER#123        | ORDER#456       | total, status, ...
USER#123        | ORDER#789       | total, status, ...
ORDER#456       | METADATA        | userId, items, ...

Parece estranho no início, mas a vantagem é buscar tudo de um usuário com uma única query:

// Busca usuário + todos os pedidos dele
const result = await docClient.query({
  TableName: 'minha-tabela',
  KeyConditionExpression: 'PK = :pk',
  ExpressionAttributeValues: {
    ':pk': `USER#${userId}`,
  },
});

Cold Start - O Elefante na Sala

Cold start é quando Lambda precisa inicializar do zero. Pode adicionar 100ms a 1s+ na resposta.

O que faço pra minimizar:

  1. Manter as Lambdas pequenas - Menos código = inicialização mais rápida
  2. Usar Node.js ou Python - Java e .NET têm cold start maior
  3. Provisioned Concurrency pra endpoints críticos:
functions:
  checkout:
    handler: src/handlers/checkout.handler
    provisionedConcurrency: 5  # Mantém 5 instâncias "quentes"

Provisioned Concurrency custa mais, então uso só onde latência importa (checkout, login).

Monitoramento que Funciona

Não adianta ter tudo rodando se você não sabe quando quebra. O mínimo:

# CloudWatch Alarm pra erros
resources:
  Resources:
    ErrorAlarm:
      Type: AWS::CloudWatch::Alarm
      Properties:
        AlarmName: ${self:service}-errors
        MetricName: Errors
        Namespace: AWS/Lambda
        Statistic: Sum
        Period: 300
        EvaluationPeriods: 1
        Threshold: 5
        ComparisonOperator: GreaterThanThreshold

Também ativo X-Ray pra rastrear requests:

provider:
  tracing:
    lambda: true
    apiGateway: true

Custos Reais

Vou ser específico porque “serverless é barato” é muito vago.

Pra uma API com 1 milhão de requests/mês:

RecursoUsoCusto
API Gateway1M requests~$3.50
Lambda1M invocações, 200ms média~$0.40
DynamoDB1M writes, 5M reads~$2.50
CloudWatchLogs básicos~$5.00
Total~$11/mês

Compara com um EC2 t3.small rodando 24/7: ~$15/mês. Mas serverless escala automaticamente e você não gerencia servidor.

Agora, se você tem 100M requests/mês constantes, a conta muda. Aí EC2 ou Fargate provavelmente sai mais barato.

Quando Migrei de Volta pra Container

Tive um caso onde comecei serverless e migrei pra ECS Fargate depois.

O motivo: a aplicação precisava de conexão persistente com banco PostgreSQL. Lambda abre/fecha conexão a cada invocação. Com tráfego alto, o banco não aguentava tantas conexões.

Tinha a opção de usar RDS Proxy, mas adicionava mais um componente (e custo). No fim, Fargate com connection pooling resolveu melhor.

Minha Stack Serverless Atual

Quando decido ir de serverless, essa é a stack:

  • API Gateway - REST ou HTTP (HTTP é mais barato)
  • Lambda - Node.js 20
  • DynamoDB - On-demand billing
  • SQS - Pra processamento assíncrono
  • EventBridge - Pra eventos entre serviços
  • Step Functions - Pra workflows complexos

Funciona bem, custo baixo, e não preciso me preocupar com servidor.


Quer discutir se serverless faz sentido pro seu caso? Me chama no LinkedIn.

Gostou do conteudo?

Descubra como implementar IA no seu negocio com uma analise gratuita.

Agendar Analise Gratuita

Pronto para automatizar seu atendimento?

Agende uma analise gratuita e descubra como IA pode transformar seu negocio.

Agendar Analise Gratuita