De reglas de negocio a restricciones verificables
Una regla de negocio describe una condición que debe cumplirse para que los datos representen correctamente la realidad (por ejemplo, “el email de un cliente no se repite”). En bases de datos, el objetivo es convertir esas reglas en restricciones verificables para que el sistema pueda impedir (o detectar) datos inválidos. La idea práctica es: si una regla puede expresarse como una condición sobre columnas, filas o relaciones, debería implementarse como restricción lo más cerca posible de la base de datos.
Tipos comunes de reglas y su traducción
- Unicidad: “No puede haber dos X con el mismo Y”. Se traduce a
UNIQUE(o índice único) sobre una o varias columnas. - Obligatoriedad: “Y es obligatorio”. Se traduce a
NOT NULL(y, si aplica, a una FK obligatoria). - Rangos y límites: “La cantidad debe ser mayor que 0”, “la fecha fin debe ser posterior a la fecha inicio”. Se traduce a
CHECKo validación en capa de aplicación cuando el motor no lo soporta bien. - Dependencias: “Si A entonces B”. Puede ser
CHECKsi depende de columnas de la misma fila; si depende de otras filas/tablas, suele requerir triggers, constraints avanzadas o lógica transaccional en la aplicación. - Compatibilidades: “Solo se permiten combinaciones válidas” (p. ej., tipo de documento compatible con país). Se modela con tablas de referencia y FKs, o con
CHECKsi el dominio es pequeño y estable.
Guía práctica paso a paso para convertir reglas en restricciones
Paso 1: Escribe la regla en lenguaje claro y sin ambigüedades
Antes de pensar en SQL, formula la regla con: sujeto, condición, alcance y excepciones. Ejemplo: “Para cada cliente, el email debe ser único en todo el sistema. Se permite email nulo solo si el cliente no ha aceptado comunicaciones”.
Paso 2: Identifica el tipo de restricción y el nivel de datos afectado
Preguntas rápidas: ¿afecta a una sola columna (dominio)? ¿a una fila (entidad)? ¿a relaciones entre tablas (referencial)? ¿a múltiples filas (reglas globales)?
Paso 3: Decide dónde implementarla (prioridad: BD > DDL > validaciones)
Regla operativa: implementa en la base de datos lo que sea invariante y deba cumplirse sin importar la aplicación. Deja en la aplicación lo que sea experiencia de usuario (mensajes, flujos) o lo que el motor no pueda garantizar de forma segura.
Paso 4: Elige el mecanismo concreto
- DDL:
PRIMARY KEY,UNIQUE,NOT NULL,CHECK,FOREIGN KEY. - Modelo lógico: definición de claves, opcionalidad, dominios, tablas de referencia, restricciones de combinación.
- Validaciones: reglas complejas, dependientes del contexto, o que requieren consultas (p. ej., solapamientos), idealmente dentro de transacciones.
- Triggers / procedimientos: cuando necesitas garantizar reglas multi-fila/multi-tabla en la BD y no hay constraints declarativas suficientes.
Paso 5: Prueba con casos límite
Para cada regla, define ejemplos válidos e inválidos (incluye nulos, valores extremos, concurrencia). Esto ayuda a detectar “huecos” típicos: nulos que evaden UNIQUE, comparaciones de fechas, o inserciones concurrentes.
- Escuche el audio con la pantalla apagada.
- Obtenga un certificado al finalizar.
- ¡Más de 5000 cursos para que explores!
Descargar la aplicación
Integridad: entidad, referencial y dominio (qué es y dónde se implementa)
| Tipo de integridad | Qué garantiza | Ejemplos | Dónde se implementa |
|---|---|---|---|
| Integridad de entidad | Cada entidad tiene identidad única y no nula | PK no nula; identificadores alternativos únicos | Modelo lógico (definir PK/AK), DDL (PRIMARY KEY, UNIQUE, NOT NULL) |
| Integridad referencial | Las referencias entre tablas son válidas | Una línea de pedido referencia a un pedido existente; reglas ON DELETE | Modelo lógico (relaciones obligatorias/opcionales), DDL (FOREIGN KEY) |
| Integridad de dominio | Los valores pertenecen al conjunto permitido | Estados permitidos; rangos; formatos; unidades | Modelo lógico (dominios/tablas catálogo), DDL (CHECK, tipos, FKs a catálogos), validaciones (formatos complejos) |
Nota práctica: aunque muchas reglas pueden duplicarse en aplicación para mejorar mensajes, la fuente de verdad de la integridad debería ser la base de datos cuando el dato es compartido por múltiples servicios o procesos.
Restricciones típicas y ejemplos en DDL
Unicidad
Regla: “El número de documento es único por tipo”.
CREATE TABLE cliente ( cliente_id BIGINT PRIMARY KEY, tipo_doc VARCHAR(10) NOT NULL, nro_doc VARCHAR(30) NOT NULL, CONSTRAINT uq_cliente_doc UNIQUE (tipo_doc, nro_doc));Detalle: la unicidad compuesta evita duplicados por combinación. Si se permite NULL en alguna columna, revisa el comportamiento del motor (en muchos, múltiples nulos no violan UNIQUE).
Obligatoriedad
Regla: “Toda línea de pedido debe tener cantidad y precio”.
CREATE TABLE pedido_linea ( pedido_linea_id BIGINT PRIMARY KEY, pedido_id BIGINT NOT NULL, producto_id BIGINT NOT NULL, cantidad NUMERIC(12,2) NOT NULL, precio_unitario NUMERIC(12,2) NOT NULL);Rangos y consistencia intra-fila
Regla: “Cantidad > 0 y descuento entre 0 y 100”.
ALTER TABLE pedido_linea ADD CONSTRAINT ck_linea_cantidad CHECK (cantidad > 0), ADD CONSTRAINT ck_linea_descuento CHECK (descuento_pct BETWEEN 0 AND 100);Regla: “fecha_fin > fecha_inicio”.
ALTER TABLE reserva ADD CONSTRAINT ck_reserva_fechas CHECK (fecha_fin > fecha_inicio);Dependencias (si A entonces B)
Regla: “Si el cliente es empresa, el campo razón social es obligatorio; si es persona, el campo apellido es obligatorio”.
ALTER TABLE cliente ADD CONSTRAINT ck_cliente_tipo_datos CHECK ( (tipo_cliente = 'EMPRESA' AND razon_social IS NOT NULL AND apellido IS NULL) OR (tipo_cliente = 'PERSONA' AND apellido IS NOT NULL AND razon_social IS NULL));Si la dependencia involucra otras tablas (por ejemplo, “si el producto es perecedero, el lote debe existir”), suele resolverse con una FK a una tabla de lotes y/o triggers cuando la condición depende de atributos en otra tabla.
Compatibilidades mediante tablas de referencia
Regla: “El tipo de documento permitido depende del país”. En lugar de un CHECK enorme, crea una tabla de compatibilidad y referencia por FK.
CREATE TABLE pais ( pais_id BIGINT PRIMARY KEY, nombre VARCHAR(100) NOT NULL);CREATE TABLE tipo_doc ( tipo_doc_id BIGINT PRIMARY KEY, codigo VARCHAR(10) NOT NULL UNIQUE);CREATE TABLE pais_tipo_doc ( pais_id BIGINT NOT NULL, tipo_doc_id BIGINT NOT NULL, PRIMARY KEY (pais_id, tipo_doc_id), FOREIGN KEY (pais_id) REFERENCES pais(pais_id), FOREIGN KEY (tipo_doc_id) REFERENCES tipo_doc(tipo_doc_id));Luego, para garantizar compatibilidad en cliente, puedes modelar cliente(pais_id, tipo_doc_id) y usar una FK compuesta hacia pais_tipo_doc(pais_id, tipo_doc_id) si el motor lo soporta y el diseño encaja.
Modelado de restricciones complejas: patrones y alternativas
Ejemplo 1: “Un pedido debe tener al menos una línea”
Esta regla es inter-filas (un pedido se valida contra sus líneas) y además tiene un matiz transaccional: durante la creación, puede existir un instante en que el pedido se inserta antes que sus líneas. Por eso, no siempre es viable como constraint inmediata.
Alternativa A: Control transaccional en la aplicación (recomendado en muchos casos)
- Crear pedido y líneas en una misma transacción.
- No confirmar (
COMMIT) si el conteo de líneas es 0. - Ventaja: simple, portable, buen control de mensajes.
- Riesgo: si hay múltiples aplicaciones/procesos, todos deben respetarlo.
Alternativa B: Estado del pedido + transición controlada
Modela un atributo de estado y restringe que solo pedidos en estado “CONFIRMADO” requieren al menos una línea. Así permites “BORRADOR” sin líneas.
ALTER TABLE pedido ADD COLUMN estado VARCHAR(20) NOT NULL;ALTER TABLE pedido ADD CONSTRAINT ck_pedido_estado CHECK (estado IN ('BORRADOR','CONFIRMADO','CANCELADO'));La regla “confirmar” se implementa en un procedimiento/servicio: al cambiar a CONFIRMADO, valida que existan líneas. Ventaja: refleja el proceso real; evita constraints imposibles en inserciones parciales.
Alternativa C: Trigger en la base de datos
Un trigger puede impedir que un pedido quede “confirmado” sin líneas, o impedir el borrado de la última línea si el pedido está confirmado. Ventaja: garantía centralizada. Coste: complejidad, pruebas, impacto en rendimiento y mantenimiento.
Ejemplo 2: “Una reserva no puede solaparse”
Regla: para un mismo recurso (habitación, sala, vehículo), los intervalos [inicio, fin) no deben solaparse. Esta regla es multi-fila y sensible a concurrencia.
Alternativa A: Constraint avanzada del motor (si existe)
Algunos motores ofrecen mecanismos específicos (por ejemplo, restricciones de exclusión con rangos). Si tu motor lo soporta, suele ser la opción más robusta y eficiente.
Alternativa B: Validación transaccional con bloqueo
Patrón general: al insertar/actualizar una reserva, ejecutar una consulta que busque solapamientos y asegurar aislamiento adecuado.
-- Pseudocódigo SQL (la sintaxis exacta depende del motor)BEGIN;-- 1) Bloquear reservas del recurso (o usar un nivel de aislamiento alto)SELECT 1 FROM reserva WHERE recurso_id = :recurso_id FOR UPDATE;-- 2) Verificar solapamientoSELECT COUNT(*) FROM reservaWHERE recurso_id = :recurso_id AND :inicio < fecha_fin AND :fin > fecha_inicio;-- 3) Si count = 0, insertar/actualizar; si no, abortarCOMMIT;Ventaja: portable. Riesgo: si no se maneja bien el aislamiento, pueden colarse solapamientos por condiciones de carrera.
Alternativa C: “Calendario” por unidades discretas
Si las reservas son por días (o franjas fijas), puedes modelar una tabla de ocupación por unidad (recurso + fecha + franja) con UNIQUE. Ventaja: constraints simples. Coste: más filas y lógica para expandir intervalos.
Checklist de implementación: dónde vive cada regla
- Siempre en DDL cuando sea posible: PK, FK, NOT NULL, UNIQUE, CHECK simples.
- En el modelo lógico: dominios, catálogos, compatibilidades, claves alternativas, opcionalidades, y notas de reglas complejas.
- En validaciones/procedimientos: reglas multi-fila, dependientes de estado, o que requieren consultas (solapamientos, “al menos una línea”, cupos, límites por período).
- En la aplicación además (duplicado intencional): para mensajes amigables y validación temprana, sin reemplazar la garantía de BD cuando el dato es compartido.
Documentación y trazabilidad de reglas de negocio
Cómo escribir reglas en lenguaje claro
Usa una plantilla consistente para evitar ambigüedad:
- ID: RB-XXX
- Enunciado: “Para cada …, debe …”
- Definiciones: qué significa cada término (p. ej., “solapamiento”)
- Alcance: tabla/entidad afectada, contexto (estado, fechas)
- Excepciones: casos permitidos
- Severidad: error (bloquea) vs advertencia (permite)
- Implementación: DDL/trigger/servicio, y referencia al artefacto (constraint name, script, endpoint)
- Casos de prueba: ejemplos válidos/ inválidos
Ejemplo de regla documentada y vinculada al modelo
| Campo | Contenido |
|---|---|
| ID | RB-012 |
| Enunciado | Para cada cliente, la combinación (tipo_doc, nro_doc) debe ser única. |
| Alcance | Tabla cliente |
| Implementación | DDL: CONSTRAINT uq_cliente_doc UNIQUE (tipo_doc, nro_doc) |
| Casos de prueba | Inválido: dos clientes con mismo tipo_doc y nro_doc. Válido: mismo nro_doc con distinto tipo_doc. |
Vincular reglas a elementos del modelo (trazabilidad)
Para asegurar trazabilidad, enlaza cada regla a:
- Elemento del modelo lógico: tabla/columna/relación afectada (por nombre estable).
- Restricción física: nombre de constraint (
ck_...,uq_...,fk_...) o trigger/procedimiento. - Historia de cambios: versión de la regla y motivo (sin narrativas largas; solo lo necesario).
Recomendación práctica: adopta una convención de nombres que incluya el ID de regla cuando sea útil, por ejemplo ck_reserva_fechas__RB_021, y mantén un registro (tabla o documento) que mapee RB-021 → constraint/trigger/servicio. Esto facilita auditorías, mantenimiento y pruebas automatizadas.