Key overloading en DynamoDB
Si vienes de SQL, una columna significa una cosa para siempre: orders.created_at es
siempre una fecha, users.email es siempre un email. El key overloading tira eso
por la borda. Das a la clave de partición y de ordenación nombres genéricos — pk,
sk — y dejas que cada tipo de item vierta un significado distinto en ellas. Una tabla,
muchas entidades, una sola forma.
¿Qué es el key overloading en DynamoDB?
El key overloading consiste en almacenar muchos tipos de entidad en una sola tabla bajo nombres de clave genéricos como pk/sk, codificando el tipo en el valor (USER#u_3001, INVOICE#2026-0014). El nombre del atributo se mantiene neutral para que usuarios, facturas y eventos compartan una misma partición; el valor lleva el tipo, y un prefijo en la clave de ordenación permite que un único Query extraiga cada entidad usando begins_with.
- Nombres de clave genéricos, valores tipados. Nombra tus claves
pk/sky pon el tipo de entidad en el valor:pk = "TENANT#acme",sk = "USER#u_3001". El nombre es tonto; el valor lleva el tipo. - Es lo que hace funcionar el single-table design. Sin overloading, una tabla
compartida no es más que un cajón de sastre. Con él, cada entidad vive en una
partición que puedes consultar con
Query. begins_withes la recompensa. Un prefijo de tipo en la clave de ordenación deja que unQueryextraiga una entidad entera, o una porción de ella, sinScany sin filtro.- El coste: legibilidad. Un volcado crudo de
pk/skno te dice nada. Necesitas un visor que decodifique los prefijos, o estarás entrecerrando los ojos ante cadenas.
Por qué los nombres genéricos ganan a los reales
DynamoDB tiene exactamente dos atributos de clave por tabla, y un Query solo puede
apuntar a una única clave de partición. Así que si nombras tu clave userId, solo los
items de usuario pueden vivir limpiamente en esa tabla — todo lo demás tiene que fingir
un userId o mudarse a su propia tabla.
El overloading esquiva eso. Un nombre neutro como pk no se compromete con ninguna
entidad, así que un usuario, una factura y un evento de auditoría pueden compartir el
mismo atributo de clave y la misma tabla. El valor, no el nombre del atributo, dice
qué es el item.
Este es el movimiento que convierte el single-table design de teoría en algo que realmente puedes consultar. La tabla compartida es el contenedor; el overloading es lo que permite que entidades distintas coexistan dentro de ella.
Un ejemplo multi-tenant
Supón que gestionas un producto SaaS de facturación. Cada tenant tiene miembros, facturas y un rastro de auditoría. En lugar de tres tablas, ponlo todo en una y sobrecarga las claves:
| pk | sk | attributes |
|---|---|---|
| TENANT#acme | META | name="Acme Inc", plan="team" |
| TENANT#acme | USER#u_3001 | email, role="admin" |
| TENANT#acme | USER#u_3002 | email, role="member" |
| TENANT#acme | INVOICE#2026-0014 | amount_cents, status="paid" |
| TENANT#acme | INVOICE#2026-0015 | amount_cents, status="open" |
| TENANT#acme | EVENT#2026-06-23T09:12Z | actor="u_3001", action="invite" |
Cada fila comparte pk = "TENANT#acme", así que forman una item collection — todas
co-ubicadas, todas alcanzables en una sola lectura de partición.
El prefijo de la clave de ordenación hace el trabajo real. Agrupa entidades y las ordena.
Consulta la colección sobrecargada
Como el tipo vive en el prefijo de la clave de ordenación, begins_with corta la
partición por entidad sin escanear nada:
Query pk = "TENANT#acme" -- el tenant entero, cada tipo
Query pk = "TENANT#acme" AND begins_with(sk, "USER#") -- solo miembros
Query pk = "TENANT#acme" AND begins_with(sk, "INVOICE#") -- solo facturas
Pagas solo por los items que la condición coincide, no por toda la partición — lo
opuesto a un Scan filtrado, donde pagas por leer filas que
luego descartas. AWS llama a esto una condición de clave; se ejecuta sobre las
claves antes de que cualquier dato salga de la partición.
Si construyes esa condición begins_with a mano, acierta los tags de tipo — un
USERS# errante en lugar de USER# no devuelve nada, en silencio. El
expression builder genera el
KeyConditionExpression y el mapa ExpressionAttributeValues para que los prefijos
coincidan con lo que realmente escribiste.
Sobrecarga el índice también
El mismo truco aplica a un GSI. Dale nombres de clave genéricos — gsi1pk, gsi1sk —
y deja que cada entidad escriba lo que necesite. Un índice responde entonces patrones
que la tabla base no puede.
| pk | sk | gsi1pk | gsi1sk |
|---|---|---|---|
| TENANT#acme | INVOICE#2026-0015 | STATUS#open | 2026-06-30 |
| TENANT#acme | INVOICE#2026-0014 | STATUS#paid | 2026-06-12 |
| TENANT#beta | INVOICE#2026-0099 | STATUS#open | 2026-06-25 |
Ahora Query gsi1 WHERE gsi1pk = "STATUS#open" lista cada factura abierta de todos los
tenants, ordenada por fecha de vencimiento — una vista entre particiones que las
claves de la tabla base, limitadas al tenant, nunca podrían servir. Una entidad
diferente puede reutilizar gsi1 con su propio significado (digamos
gsi1pk = "ROLE#admin"), así que un índice cubre varias lecturas. Solo recuerda que un
GSI es eventualmente consistente — sus escrituras van por detrás
de la tabla base.
Hazlo en DynoTable
Las claves sobrecargadas en crudo son hostiles de leer: INVOICE#2026-0015 y
EVENT#2026-06-23T09:12Z se confunden en una lista plana. Un visor que agrupa por
partición y resalta los prefijos convierte el cajón de sastre de nuevo en entidades.

Trampas
- Elige los delimitadores una vez y no los cambies nunca.
#es la convención. Mezclar#y:entre entidades rompebegins_withde maneras de las que nada te avisa. - No sobrecargues valores que necesitan matemática de rango. Una clave de
ordenación
INVOICE#2026-0015se ordena léxicamente, no numéricamente — rellena con ceros los ids y usa fechas ISO-8601 para que el orden de cadenas coincida con el orden que quieres decir. - Reserva el espacio de nombres de prefijos. Dos tipos de entidad que ambos empiezan
con
USER(digamosUSER#yUSERGROUP#) colisionarán bajobegins_with(sk, "USER"). Haz que los prefijos sean inequívocos desde el primer carácter. - Planifica la lectura antes que las claves. El overloading sirve patrones de acceso que has enumerado. Si aún no conoces tus lecturas, mira primero single-table design — las claves van por detrás de las consultas.
Traza un mapa de una partición, luego descarga DynoTable para navegar tus
propias claves sobrecargadas y ver un Query extraer un tenant entero de una vez.


