El modelado de datos es un proceso que mapea entidades, atributos y relaciones antes de decidir cómo la base de datos los almacenará. Las entidades son la parte fácil — todo producto conoce sus usuarios, pedidos y posts. El oficio está en las relaciones: qué registros apuntan a cuáles, cuántos, y dónde vive físicamente esa arista. Acierta las aristas y las queries se escriben solas; equivócate y cada feature pelea contra el esquema.
Puntos clave
| Pregunta | Respuesta |
|---|---|
| Los tres niveles | Conceptual (qué) → lógico (estructura) → físico (motor) |
| Las tres aristas | Uno-a-uno · uno-a-muchos · muchos-a-muchos |
| Herramientas relacionales | Claves foráneas; tablas de unión para muchos-a-muchos |
| Herramientas de documentos | Embeber, Pointers (referencias), Relations/arrays de IDs |
| La regla moderna | Modela para los patrones de acceso — lo que se lee junto vive junto |
Las mismas relaciones, en ambos mundos
Primero el mundo relacional — la clave foránea carga el uno-a-muchos, y el muchos-a-muchos debe descomponerse a través de una tabla de unión:
-- 1:N — la clave foránea vive en el lado de "muchos"
CREATE TABLE books (
id bigint PRIMARY KEY,
title text NOT NULL,
author_id bigint NOT NULL REFERENCES authors(id) ON DELETE CASCADE
);
-- M:N — ninguna FK sola puede expresarlo; la tabla de unión guarda ambas claves
CREATE TABLE book_genres (
book_id bigint REFERENCES books(id),
genre_id bigint REFERENCES genres(id),
added_at timestamptz DEFAULT now(), -- las uniones pueden llevar atributos
PRIMARY KEY (book_id, genre_id) -- clave compuesta: una arista, una vez
);
Mundo de documentos, el mismo modelo: la arista uno-a-muchos es un Pointer tipado, la muchos-a-muchos es una Relation — sin tabla de unión que inventar, y la arista se declara en el código de la aplicación:
// JavaScript / Node.js — Back4app JS SDK
// One-to-many: a Pointer. Many-to-many: a Relation.
const author = await new Parse.Query('Author').get(authorId);
const book = new Parse.Object('Book');
book.set('title', 'Dune');
book.set('author', author); // Pointer — typed one-to-many edge
await book.save();
const genres = book.relation('genres'); // Relation — many-to-many, unbounded
genres.add([sciFi, classics]);
await book.save(); // Flutter / Dart — Back4app Flutter SDK
// One-to-many: a Pointer. Many-to-many: a Relation.
final book = ParseObject('Book')
..set('title', 'Dune')
..set('author', author.toPointer()); // Pointer — typed one-to-many edge
await book.save();
book.addRelation('genres', [sciFi, classics]); // Relation — many-to-many
await book.save(); // iOS / Swift — Back4app Swift SDK
// One-to-many: a Pointer. Many-to-many: a Relation.
var book = Book()
book.title = "Dune"
book.author = try author.toPointer() // Pointer — typed one-to-many edge
let saved = try await book.save()
let relation = try saved.relation("genres")
try await relation.add([sciFi, classics]).save() // Relation — many-to-many // Android / Kotlin — Back4app Android SDK
// One-to-many: a Pointer. Many-to-many: a Relation.
val book = ParseObject("Book").apply {
put("title", "Dune")
put("author", author) // Pointer — typed one-to-many edge
}
book.save()
val genres = book.getRelation<ParseObject>("genres") // Relation — many-to-many
genres.add(sciFi)
genres.add(classics)
book.save() Conceptual vs. lógico vs. físico: los tres niveles
| Dimensión | Conceptual | Lógico | Físico |
|---|---|---|---|
| Pregunta que responde | Qué existe, cómo se relaciona | Qué campos, qué claves | Cómo se almacena, qué tan rápido |
| Audiencia | Todos | Diseñadores | Ingenieros + el motor |
| Contiene | Entidades, relaciones | + atributos, tipos, cardinalidad | + índices, constraints, particiones |
| Cambia cuando | El negocio cambia | Los requisitos se refinan | El motor o la escala cambian |
La disciplina que los niveles imponen: no discutas sobre índices mientras el equipo todavía no se pone de acuerdo en qué es un “pedido”.
Cada relación, cada implementación
La tabla que la SERP nunca construyó — cada tipo de arista con su implementación en ambos mundos:
| Relación | Ejemplo | Implementación relacional | Implementación en documentos |
|---|---|---|---|
| Uno-a-uno | Usuario ↔ perfil | FK con UNIQUE, o la misma fila | Embébelo — casi siempre |
| Uno-a-pocos | Persona → direcciones | Tabla hija + FK | Array embebido (acotado) |
| Uno-a-muchos | Autor → libros | FK en el lado de muchos | Pointer en el lado de muchos |
| Uno-a-enormes | Dispositivo → eventos | FK + particionado | Pointer en el hijo; nunca un array |
| Muchos-a-muchos | Libros ↔ géneros | Tabla de unión, PK compuesta | Relation o arrays de pointers |
| Auto-referencial | Empleado → gerente | FK a su propia tabla | Pointer a su propia clase |
Notación de cardinalidad, para leer diagramas: la pata de gallo dibuja una barra para “uno” y un tridente de tres puntas para “muchos”; la tradición ER escribe 1 y N sobre las líneas; UML escribe multiplicidades como 1 y *. Tres dialectos, una sola gramática.
¿Normalizar, desnormalizar o embeber?
El modelado relacional parte de la normalización — separa los datos para que cada hecho viva una sola vez, protegiendo las escrituras de anomalías. El modelado analítico y el de documentos la doblan a propósito: la desnormalización duplica campos calientes de lectura para matar joins, y embeber es el primo nativo de documentos de la desnormalización. El checklist de embeber vs. referenciar se comprime en tres preguntas: ¿se leen juntos? ¿pertenecen a un solo padre? ¿tienen tamaño acotado? Tres síes embeben; cualquier no, referencia. Y por encima de todo, la regla moderna que las guías generalistas se saltan: lista primero las queries. Un modelo es correcto cuando los patrones de acceso que debe servir son baratos — las entidades por sí solas no pueden decírtelo.
Casos de uso comunes
- Diseñar un backend nuevo. La pasada clásica: nombra las entidades, dibuja las aristas, elige implementaciones según la tabla de arriba — antes de la primera línea de código.
- El patrón de unión con atributos. Inscripciones, membresías, líneas de pedido — cuando la arista misma tiene datos (fecha, cantidad, rol), la unión (o una clase-arista con dos pointers) es una entidad de primera clase.
- Desenredar un esquema que creció. Los síntomas mapean a aristas: filas duplicadas revelan un muchos-a-muchos mal modelado; documentos inflados revelan un embed sin límite.
- Migrar entre mundos. Relacional→documentos significa volver a decidir cada FK como embeber vs. pointer; la tabla de arriba es el diccionario de traducción.
- Feeds de AI y analítica. Los warehouses quieren las aristas explícitas y estables — la deuda de modelado sale a la luz el día que intentas exportar.
¿Cómo deberías modelar cada arista? Matriz de decisión
| Elige… | Cuando… | Cuidado con… |
|---|---|---|
| Embeber | Se leen juntos, con un solo dueño, acotados | El crecimiento: los “pocos” de hoy son los miles de mañana |
| Pointer / FK | Ciclo de vida independiente, alta cardinalidad | Indéxalo — cada lookup y cada include lo pagan |
| Relation / unión | Muchos-a-muchos, o la arista lleva datos | La dirección de la query: sabe desde qué lado vas a preguntar |
| Duplicar (desnormalizar) | Un campo caliente de lectura cruza una arista | El fan-out de updates — la duplicación es una deuda con intereses |
Limitaciones y trade-offs
- Los modelos congelan supuestos. Las decisiones de cardinalidad (“un usuario tiene una dirección”) se vuelven esquema; baratas de cambiar en papel, caras después del lanzamiento — modela para la cardinalidad de mañana.
- Ambos mundos castigan la arista sin índice. Las columnas FK y los campos pointer son rutas de join; olvidar sus índices es el bug de performance silencioso más común.
- Embeber cambia integridad por localidad. Ningún motor garantiza que una copia embebida se mantenga consistente con su fuente — esa ahora es tu lógica de updates.
- Las uniones multiplican joins; las relations los esconden. Cada lectura muchos-a-muchos cruza el almacén de aristas — presupuesta la ruta de la consulta, estés en el mundo que estés.
- Modelar por patrones de acceso también cuesta. Optimizar para las queries de hoy puede casar el esquema con el producto de hoy; conserva el modelo conceptual como la verdad neutral de fondo.
El modelado de datos en Back4app
Back4app es una plataforma open-source de Backend as a Service (BaaS) que combina base de datos gestionada, APIs REST y GraphQL generadas automáticamente, autenticación, almacenamiento de archivos y funciones serverless con Cloud Code. Su vocabulario de modelado es la columna de documentos de las tablas de arriba, elevada a primera clase: los Pointers son aristas uno-a-muchos tipadas, las Relations llevan el muchos-a-muchos sin una tabla de unión hecha a mano, y los arrays cubren los pocos-acotados. Cada arista que declaras es inmediatamente recorrible — include() camina los pointers en una sola solicitud, GraphQL anida relations en una sola query — y el esquema se mantiene visible y editable en el dashboard, así el modelo físico nunca se pierde de vista del equipo que diseñó el conceptual.
Preguntas frecuentes
¿Qué es el modelado de datos?
El proceso de mapear la información de una aplicación — sus entidades, sus atributos y las relaciones entre ellos — en un diseño que una base de datos pueda almacenar. Suele avanzar por tres niveles de detalle: un modelo conceptual que nombra las entidades, un modelo lógico que agrega atributos y claves, y un modelo físico que se compromete con tablas, columnas e índices en un motor específico.
¿Cuáles son los tres tipos de modelos de datos?
Conceptual, lógico y físico — el mismo diseño con zoom creciente. El conceptual responde "qué existe y cómo se relaciona" para los stakeholders de negocio. El lógico agrega atributos, tipos y claves sin atarse a una tecnología. El físico se compromete con un motor real: tablas o colecciones, índices, constraints. Cada nivel es el anterior más decisiones.
¿Qué es una relación uno-a-muchos?
Un registro padre que se relaciona con muchos hijos, donde cada hijo pertenece exactamente a un padre — un cliente y sus pedidos, un autor y sus libros. Es la relación más común en cualquier esquema. Las bases relacionales la implementan con una clave foránea en el lado de muchos; las bases de documentos usan un array embebido para conjuntos pequeños y acotados, o un pointer al padre para todo lo demás.
¿Qué es una relación muchos-a-muchos y por qué necesita una tabla de unión?
Ambos lados se relacionan con muchos del otro — estudiantes y cursos, libros y géneros. Una sola columna de clave foránea solo puede apuntar a una fila, así que las bases relacionales descomponen el muchos-a-muchos en dos uno-a-muchos a través de una tabla de unión que guarda ambas claves (y a menudo atributos de la relación, como la fecha de inscripción). Las bases de documentos se saltan la unión: arrays de referencias, o un tipo relation dedicado, llevan la arista directamente.
¿Cómo distingo uno-a-muchos de muchos-a-muchos?
Haz la pregunta de pertenencia en ambas direcciones. "¿Puede un autor tener muchos libros?" Sí. "¿Puede un libro tener muchos autores?" Si no — uno-a-muchos, clave foránea en el libro. Si sí — muchos-a-muchos, tabla de unión o relation. Equivocarse aquí es el error de modelado clásico: un muchos-a-muchos modelado como uno-a-muchos duplica filas o pierde aristas en silencio.
¿Qué es la cardinalidad en un diagrama ER?
Cuántas instancias de una entidad pueden relacionarse con instancias de otra — uno-a-uno, uno-a-muchos, muchos-a-muchos. Los diagramas la expresan en una de tres notaciones: números y letras sobre las líneas de conexión, símbolos de pata de gallo (una barra para uno, un tridente de tres puntas para muchos), o rangos de multiplicidad como 1 y asterisco. La misma semántica, distintas convenciones de dibujo.
¿Cómo modelan relaciones las bases de documentos sin claves foráneas?
Con tres herramientas: embeber (anidar los datos relacionados dentro del documento padre — correcto cuando se leen juntos, tienen un solo dueño y están acotados), pointers o referencias (guardar el ID del documento relacionado como campo tipado — correcto para ciclos de vida independientes y alta cardinalidad), y objetos relation o arrays de IDs para muchos-a-muchos. La regla guía pasa de la normalización a los patrones de acceso: los datos que se leen juntos deben vivir juntos.
¿Cuáles son los errores más comunes de modelado de datos?
Un top cinco estable: modelar muchos-a-muchos como uno-a-muchos; olvidar los índices en campos de clave foránea y de pointer (cada join y cada lookup lo pagan); arrays embebidos sin límite en esquemas de documentos; desnormalización prematura antes de que alguna query pruebe ser lenta; y modelar solo desde las entidades sin listar las queries — los patrones de acceso — que el modelo debe servir.