Cloud, DevOps e IoT en español

Parte 7 de 22 de la serie Domótica con ESP32 y AWS desde cero

DynamoDB para IoT: cómo modelar dispositivos y lecturas de sensores

5 de octubre de 2026 · Steven Carvajal · Tutoriales

Código de este artículo en GitHub →

El Device Shadow guarda el estado actual de cada dispositivo (parte 5), pero un sistema IoT necesita guardar más cosas: qué dispositivos tiene cada casa, cómo se llaman, y las lecturas de los sensores a lo largo del tiempo. En AWS, la opción serverless más común para eso es DynamoDB.

Si vienes de bases de datos relacionales, DynamoDB obliga a pensar al revés: primero las consultas, después las tablas. En esta parte lo aplicamos a un proyecto de ejemplo.

Lo mínimo que hay que saber

Paso 1: escribir las consultas

En el proyecto de ejemplo, la app necesita:

  1. Los dispositivos de una casa, al abrir la app.
  2. Los datos de un dispositivo, al abrir su pantalla.
  3. Las lecturas de un sensor en un rango de tiempo, para una gráfica.

Paso 2: la tabla de dispositivos

Las consultas 1 y 2 se resuelven con una tabla donde todo lo de una casa comparte la clave de partición:

ClaveAtributoEjemplo
Partition keycasaIdcasa-demo
Sort keydispositivoIdluz-sala
{ "casaId": "casa-demo", "dispositivoId": "luz-sala",      "tipo": "luz",    "nombre": "Sala" }
{ "casaId": "casa-demo", "dispositivoId": "luz-cocina",    "tipo": "luz",    "nombre": "Cocina" }
{ "casaId": "casa-demo", "dispositivoId": "sensor-patio",  "tipo": "sensor", "nombre": "Patio" }

Un item por dispositivo es mejor que un solo item por casa con un arreglo dentro: modificar un dispositivo no obliga a reescribir los demás, y no te acercas al límite de 400 KB por item.

import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";

const db = DynamoDBDocumentClient.from(new DynamoDBClient({}));

const { Items } = await db.send(
  new QueryCommand({
    TableName: "dispositivos",
    KeyConditionExpression: "casaId = :c",
    ExpressionAttributeValues: { ":c": "casa-demo" },
  })
);

Con pocos dispositivos por casa, el resultado cabe en una página. Si una partición puede devolver más de 1 MB, hay que paginar con LastEvaluatedKey.

Paso 3: la tabla de lecturas

Las lecturas de sensores son otra cosa: muchas, pequeñas, y se consultan por rango de tiempo. La clave encaja así:

ClaveAtributoEjemplo
Partition keydispositivoIdsensor-patio
Sort keyts (marca de tiempo)2026-10-05T08:30:00Z
// Lecturas del sensor en las últimas 24 horas
const desde = new Date(Date.now() - 24 * 3600 * 1000).toISOString();

const { Items } = await db.send(
  new QueryCommand({
    TableName: "lecturas",
    KeyConditionExpression: "dispositivoId = :d AND ts >= :desde",
    ExpressionAttributeValues: { ":d": "sensor-patio", ":desde": desde },
  })
);

Dos detalles importantes:

Las lecturas pueden llegar a la tabla sin escribir código: una regla de AWS IoT las toma del tópico MQTT y las inserta (parte 14).

¿Una tabla o varias?

El diseño de tabla única (single-table design) mete todo en una tabla con claves genéricas (PK, SK) y prefijos como CASA# o DISP#. Ahorra consultas cuando hay muchas relaciones, pero es más difícil de leer y de cambiar.

Para un proyecto pequeño, una tabla por tipo de dato con claves claras suele ser más fácil de mantener. Lo importante no es el número de tablas, sino que cada consulta de la app sea un Query o un GetItem.

Índices: cuando necesitas buscar por otro campo

Si necesitas buscar por un atributo que no es la clave (por ejemplo, encontrar un dispositivo por su número de serie), crea un índice secundario global (GSI) con ese atributo como clave.

Un truco útil: un GSI solo incluye los items que tienen su atributo clave. Si solo algunos items lo tienen, el índice queda pequeño. Se llama índice disperso (sparse index) y es ideal para buscar "los pocos items que cumplen algo", como dispositivos pendientes de activar.

Escrituras seguras

Quién puede leer qué

Si la tabla solo se lee desde funciones Lambda, controlas el acceso en el código. Si la app lee directamente con credenciales temporales de Cognito, IAM puede limitar cada usuario a sus propias particiones con la condición dynamodb:LeadingKeys. En cualquier caso, nunca uses un identificador que envía el cliente para decidir qué datos devolver: sácalo de un token verificado (parte 11).

Cuánto cuesta

En modo bajo demanda pagas por lectura, escritura y almacenamiento, sin capacidad reservada. Para una casa con unos pocos dispositivos y lecturas cada minuto, son centavos al mes, y con TTL el almacenamiento no crece. La estimación completa está en la parte 22.

Errores comunes

Preguntas frecuentes

¿DynamoDB o Timestream para lecturas de sensores?

Para pocas lecturas y gráficas simples, DynamoDB con TTL basta y es más barato de empezar. Para análisis de series temporales a gran escala (agregaciones, ventanas de tiempo), una base de datos de series temporales es más cómoda.

¿Puedo cambiar la clave de una tabla después?

No. La clave se fija al crear la tabla. Para cambiarla hay que crear otra y migrar los datos. Por eso conviene escribir las consultas antes.

¿Necesito guardar el estado del dispositivo en DynamoDB?

No, el estado actual ya está en el Device Shadow. DynamoDB es para lo que el Shadow no guarda: nombres, configuración e historial.

Sigue leyendo