Modelo entidad-relación
Antes de escribir una tabla conviene dibujar de qué habla el sistema. El modelo entidad-relación existe para discutir el dominio con quien lo conoce, no para documentar la base ya hecha.
La mayoría de los problemas caros de una base de datos no son de rendimiento: son de modelado, y se decidieron el primer día. Una entidad que debió ser dos, o una relación cuya cardinalidad se leyó mal, se paga en cada consulta y en cada migración posterior.
Entidades, atributos y relaciones
El modelo tiene tres piezas. Las entidades son las cosas de las que el sistema guarda información: cliente, factura, producto. Los atributos son sus datos. Las relaciones son los vínculos entre entidades: un cliente emite facturas, una factura tiene ítems.
La prueba práctica para separar entidad de atributo es preguntarse si la cosa tiene vida propia —identidad, atributos propios, historia— o si sólo describe a otra. Una dirección suele ser un atributo; si hay que guardar varias por cliente y saber cuál está vigente, ya es una entidad.
Antes de seguir, predecí
La cardinalidad es la decisión más cara
La cardinalidad dice cuántas instancias de cada lado participan: uno a uno, uno a muchos, muchos a muchos. Es la decisión que más consecuencias tiene y la que más se discute mal.
Conviene enunciarla en voz alta y en las dos direcciones: “una factura pertenece a un solo cliente; un cliente puede tener muchas facturas”. Y siempre preguntar por el futuro, no por hoy: “¿puede un producto tener más de un proveedor alguna vez?”. Esa pregunta cuesta un minuto ahora y una migración con datos en producción después.
Tres entidades, sin relaciones todavía. Cada una tiene identidad propia: se puede hablar de «el cliente 7» sin mencionar nada más.
Obligatoria u opcional
Además de cuántos, importa si la participación es obligatoria u opcional. ¿Puede existir una factura sin ítems? ¿Un empleado sin departamento asignado?
Eso se traduce directo en si la columna admite nulos, y define qué estados intermedios son válidos. Un modelo que no distingue obligatorio de opcional termina en una base llena de nulos que significan cosas distintas: “no se cargó todavía”, “no aplica” y “se perdió el dato” conviviendo en la misma columna.
El pasaje a tablas es casi mecánico
El pasaje a tablas es casi mecánico. Cada entidad es una tabla con su clave primaria. Una relación uno a muchos se resuelve poniendo la clave foránea del lado “muchos”: la factura guarda el identificador del cliente.
Una relación muchos a muchos necesita una tabla propia, con las dos claves foráneas. Y ahí aparece algo útil: esa tabla intermedia casi siempre termina teniendo atributos —cantidad, precio pactado, fecha— y eso confirma que era una entidad disfrazada de relación.
Los errores que se repiten
Los errores típicos se repiten. Modelar un estado como muchas columnas booleanas en vez de un atributo con dominio acotado. Guardar listas en un campo de texto separadas por comas, que rompe toda consulta posterior. Reusar una misma tabla para dos conceptos parecidos porque “comparten casi todas las columnas”.
Y el más caro: no modelar el tiempo. Si el precio de un producto cambia, ¿la factura vieja tiene que seguir mostrando el precio de entonces? Casi siempre sí, y eso significa copiar el valor al momento de emitir, no apuntar al producto.
El diagrama es una herramienta de conversación
El diagrama no es documentación: es una herramienta de conversación. Su público es quien conoce el negocio y no sabe SQL, y su valor está en que esa persona pueda mirarlo y decir “eso está mal, un cliente puede tener dos cuentas”.
Por eso conviene mantenerlo en el vocabulario del dominio y sin detalles de implementación. Una vez que la base existe, el esquema es la verdad; el diagrama sirve para las decisiones nuevas.
Del diagrama a las tablas
CREATE TABLE customers (
id BIGSERIAL PRIMARY KEY,
cuit VARCHAR(13) NOT NULL UNIQUE, -- clave natural, como restricción
name TEXT NOT NULL
);
CREATE TABLE invoices (
id BIGSERIAL PRIMARY KEY,
customer_id BIGINT NOT NULL REFERENCES customers(id), -- el lado «muchos»
issued_at TIMESTAMPTZ NOT NULL,
UNIQUE (customer_id, issued_at) -- una regla de negocio, hecha cumplir por el motor
);
-- La relación muchos a muchos, con sus propios atributos
CREATE TABLE invoice_items (
invoice_id BIGINT NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
product_id BIGINT NOT NULL REFERENCES products(id),
quantity INTEGER NOT NULL CHECK (quantity > 0),
unit_price NUMERIC(12,2) NOT NULL, -- el precio del día, congelado
PRIMARY KEY (invoice_id, product_id)
);
CREATE INDEX ON invoices (customer_id); -- la foránea no se indexa solaTres traducciones a la vista. El uno a muchos es una columna con REFERENCES en el lado «muchos».
El muchos a muchos es una tabla propia, con clave primaria compuesta. Y el unit_price en el ítem
—no en el producto— es la decisión de modelado que más se discute y la más importante: el precio
de una venta es un dato histórico y tiene que quedar congelado, aunque el del producto cambie
mañana.
El ON DELETE CASCADE del ítem contra su ausencia en product_id también es una decisión: borrar
una factura borra sus ítems, pero un producto usado en una factura no se puede borrar, y eso está
bien.
Las decisiones que cuestan caro después
| Decisión | Lo cómodo hoy | Lo que conviene | Por qué |
|---|---|---|---|
| Clave primaria | la natural: el CUIT | una sustituta, y la natural como UNIQUE | las claves naturales cambian y se corrigen |
| Cardinalidad dudosa | uno a muchos | preguntar por el futuro | pasar de 1:N a N:M es una migración |
| Borrado | DELETE | una marca de baja | lo borrado no se puede auditar |
| Un campo con varios valores | una lista separada por comas | una tabla | no se puede indexar ni consultar |
| Atributos que varían por tipo | columnas nulas para todos | tablas por tipo o JSON validado | una tabla con 40 columnas nulas no dice nada |
Cierre
Entidades, atributos y relaciones con su cardinalidad y su obligatoriedad. El pasaje a tablas es mecánico; lo difícil es antes: separar entidad de atributo, leer bien la cardinalidad, decidir qué es opcional y modelar el tiempo donde el negocio lo necesita.
Autoevaluación
¿Lo entendiste?
Práctica