Explica cómo relacionar documentos mediante identificadores cuando las entidades tienen ciclos de vida independientes, cardinalidad alta o crecimiento no acotado.
La orden conoce el identificador del cliente, pero el cliente vive como documento independiente:
Texto
orders.customerId
↓
customers._id
Una referencia en MongoDB es solo un valor con significado acordado por la aplicación. No crea automáticamente una foreign key, no comprueba que el destino exista y no ejecuta cascades cuando se elimina el documento relacionado.
Referencing evita que una entidad independiente deba copiarse completa dentro de cada documento que la utiliza. Es especialmente útil cuando:
la entidad tiene su propio lifecycle;
muchas entidades la comparten;
la relación puede crecer sin límite;
el dato cambia con frecuencia;
se necesita consultar o administrar por separado;
embeber produciría documentos grandes o hotspots;
la duplicación generaría fan-out de actualizaciones costoso.
Ejemplo: un producto existe aunque ninguna orden lo haya vendido todavía. Tiene stock, categoría, precio actual, imágenes y reglas propias. Por eso el producto suele mantenerse independiente, aunque una orden embeberá algunos de sus valores como snapshot.
Cuando varias escrituras deben mantenerse como una unidad, una transacción puede proteger el cambio. No transforma la referencia en foreign key permanente.
Una dangling reference apunta a un documento inexistente.
Puede aparecer por:
eliminación manual;
importación incompleta;
operación parcialmente fallida;
race condition;
migración;
restauración selectiva;
bug que aceptó un ID incorrecto.
Consecuencias:
$lookup devuelve array vacío;
la aplicación falla al asumir que existe;
reportes pierden datos;
permisos pueden quedar inconsistentes.
La lectura debe manejar ausencia explícitamente:
TypeScript
const customer =await customers.findOne({
_id: order.customerId,
businessId: order.businessId
});if(!customer){// Decide según el dominio: error, placeholder o registro de inconsistencia.}
No utilices non-null assertions para esconder el caso.
MongoDB no ejecuta ON DELETE CASCADE automáticamente.
Antes de eliminar un documento, define:
¿se prohíbe si tiene referencias?
¿se eliminan hijos?
¿se conserva historia?
¿se anonimiza?
¿se marca como deleted?
¿se ejecuta un job asíncrono?
¿se necesita transacción?
Ejemplo: eliminar un producto no debería borrar items históricos de órdenes. Esos items son snapshots. En cambio, podría eliminar asociaciones de catálogo que no tienen valor histórico.
$lookup combina documentos, pero no vuelve relacional el modelo ni elimina el coste de consultar ambas colecciones.
Evalúa:
cardinalidad del resultado;
selectividad antes del join;
índices del lado extranjero;
tamaño de documentos;
memoria del pipeline;
sharding y versión;
frecuencia del access pattern.
Un $lookup ocasional puede ser correcto. Utilizarlo en cada request para reconstruir aggregates que siempre se leen juntos puede indicar un boundary deficiente.
El ID conecta con el cliente actual. El nombre conserva historia. Esta estrategia debe documentar qué campos son snapshots y cuáles se consideran current state.
En un sharded cluster, las referencias pueden apuntar a documentos de otros shards. MongoDB no garantiza colocación conjunta por el simple hecho de compartir IDs.
El diseño debe considerar:
shard key de cada colección;
queries targeted;
$lookup soportado por la versión y topología;
transacciones distribuidas;
tenant locality;
coste de red entre shards.
Una referencia frecuente entre colecciones con shard keys incompatibles puede elevar el coste operacional.
Referencing preserva independencia y controla crecimiento, pero traslada al diseño la responsabilidad de composición, integridad, cascades y consistencia. Una referencia no es una garantía: es una conexión lógica que debe protegerse mediante casos de uso, índices, políticas y observabilidad.
Comprueba lo aprendido
¿Por qué customerId no garantiza que exista un cliente?
¿Dónde guardarías la referencia en una relación producto-reviews ilimitada?
¿Cómo evitarías duplicados concurrentes en memberships?
¿Qué alternativas existen al patrón N+1?
¿Por qué una orden puede conservar tanto productId como nombre y precio embebidos?
¿Qué política definirías al eliminar un documento referenciado?