DEV Community

Event-Driven Architecture con Lambda y S3

Event‑Driven Architecture (EDA) es un patrón de diseño en el cual los componentes del sistema se comunican a través de eventos. Este enfoque permite construir sistemas desacoplados, escalables y reactivos, donde cada parte del sistema responde a eventos en lugar de seguir un flujo de control predefinido.

Amazon S3 puede generar eventos cuando se sube un objeto s3:ObjectCreated:* y estos eventos pueden invocar funciones AWS Lambda automáticamente. Con este patrón puedes automatizar tareas como redimensionado, análisis, notificaciones, o incluso sobreponer una marca de agua a imágenes subidas.

En este artículo construiremos un pipeline para:

  • Procesar imágenes subidas a S3.
  • Aplicar una marca de agua.
  • Almacenarlas en otro bucket o prefijo.

Conceptos Fundamentales

Event Source y Event Target

  • Event Source: Amazon S3 que emite notificaciones.
  • Event Target: AWS Lambda que consume el evento.

Comunicación Asíncrona

Cada imagen es procesada como un evento independiente, lo que permite procesamiento paralelo, alta tolerancia y escalabilidad.

S3 Events & Lambda Triggers

S3 permite configurar notificaciones como:

"Events": ["s3:ObjectCreated:*"]
Enter fullscreen mode Exit fullscreen mode

Estas notificaciones invocan funciones Lambda cuando se produce un evento compatible.

Implementación Práctica: Aplicar Marca de Agua

Flujo

  1. Usuario sube una imagen a bucket S3.
  2. Lambda aplica marca de agua con el logo.
  3. Imagen procesada se guarda en un prefijo o bucket definido.

Arquitectura

El flujo arquitectónico básico se ve así:

Flow

Configuración S3

{
  "NotificationConfiguration": {
    "LambdaFunctionConfigurations": [
      {
        "LambdaFunctionArn": "arn:aws:lambda:region:account:function:processImage",
        "Events": ["s3:ObjectCreated:*"],
        "Filter": {
          "Key": {
            "FilterRules": [
              { "Name": "suffix", "Value": ".jpg" }
            ]
          }
        }
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Lambda Function

import { S3Client, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';
import sharp from 'sharp';

const s3 = new S3Client({ region: 'us-east-1' });

async function streamToBuffer(stream) {
  const chunks = [];
  for await (const chunk of stream) {
    chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);
  }
  return Buffer.concat(chunks);
}

export const handler = async (event) => {
  const bucket = event.Records[0].s3.bucket.name;
  const key = event.Records[0].s3.object.key;

  // Obtener logo
  const watermarkObj = await s3.send(new GetObjectCommand({
    Bucket: 'assets-bucket',
    Key: 'community-day-logo.png'
  }));
  const watermarkBuffer = await streamToBuffer(watermarkObj.Body);

  // Redimensionar logo
  const resizedWatermark = await sharp(watermarkBuffer)
    .resize({ width: 300 })
    .png()
    .toBuffer();

  // Obtener imagen original
  const imageObj = await s3.send(new GetObjectCommand({
    Bucket: bucket,
    Key: key
  }));
  const originalBuffer = await streamToBuffer(imageObj.Body);

  // Obtener metadatos de la imagen original para calcular padding
  const metadata = await sharp(originalBuffer).metadata();

  const paddingX = 20; // 20px desde el borde derecho
  const paddingY = 20; // 20px desde el borde inferior

  // Obtener dimensiones del logo redimensionado
  const logoMetadata = await sharp(resizedWatermark).metadata();

  const top = (metadata.height ?? 0) - (logoMetadata.height ?? 0) - paddingY;
  const left = (metadata.width ?? 0) - (logoMetadata.width ?? 0) - paddingX;

  // Componer con marca de agua redimensionada y padding
  const watermarked = await sharp(originalBuffer)
    .composite([
      {
        input: resizedWatermark,
        top,
        left
      },
    ])
    .jpeg()
    .toBuffer();

  // Subir imagen final con marca de agua
  await s3.send(new PutObjectCommand({
    Bucket: bucket,
    Key: `processed/mark-${key}`,
    Body: watermarked,
    ContentType: 'image/jpeg',
  }));
};
Enter fullscreen mode Exit fullscreen mode

Configuración Trigger

trigger

  1. Seleccione "Add trigger".
  2. En "Bucket", seleccione su bucket de origen.
  3. En "Event types", seleccione "All object create events".
  4. En "Recursive invocation", marque la casilla para confirmar que no se recomienda usar el mismo depósito de Amazon S3 para entrada y salida.
  5. Seleccione "Add".

trigger configs

get objects

Image with watermarked

Consideraciones

  • Idempotencia: identifica la imagen procesada para no hacerlo dos veces.
  • Observabilidad: CloudWatch Logs y métricas para seguimiento.
  • Optimización de costos: memoria adecuada, timeouts y tamaño del paquete bien definidos.

Top comments (0)