Toda aplicación necesita, tarde o temprano, guardar datos que sobrevivan al reinicio del proceso. La decisión sobre cómo y dónde persistir esos datos es una de las más influyentes y duraderas de cualquier arquitectura: condiciona el rendimiento, la escalabilidad, la consistencia y hasta el coste de operación. Durante décadas la respuesta por defecto fue "una base de datos relacional", pero la explosión de aplicaciones web a gran escala popularizó un abanico de alternativas agrupadas bajo el término NoSQL. En esta lección aprenderás las diferencias reales entre ambos mundos, qué garantías ofrecen (ACID frente a BASE), qué familias de bases NoSQL existen y, sobre todo, qué criterios objetivos usar para elegir. El objetivo no es declarar un ganador, sino que sepas razonar la decisión.
Contenido
- El modelo relacional (SQL) en esencia
- Qué significa realmente "NoSQL"
- Las cuatro familias NoSQL: documental, clave-valor, columnar y grafo
- Garantías transaccionales: ACID frente a BASE
- Tabla comparativa SQL vs NoSQL
- Criterios de elección y antipatrones
- Persistencia políglota
- El modelo relacional (SQL) en esencia
Una base de datos relacional organiza los datos en tablas (relaciones) formadas por filas y columnas, con un esquema rígido definido de antemano. Las relaciones entre tablas se expresan mediante claves foráneas, y consultamos los datos con SQL (Structured Query Language). Ejemplos: PostgreSQL, MySQL, Oracle, SQL Server.
Veamos un esquema relacional sencillo para clientes y sus pedidos:
-- Tabla de clientes: cada cliente tiene un identificador único
CREATE TABLE clientes (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
nombre VARCHAR(120) NOT NULL,
email VARCHAR(180) NOT NULL UNIQUE,
creado_en TIMESTAMP NOT NULL DEFAULT now()
);
-- Tabla de pedidos: la columna cliente_id "apunta" a clientes.id
CREATE TABLE pedidos (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
cliente_id BIGINT NOT NULL REFERENCES clientes(id),
total NUMERIC(10,2) NOT NULL CHECK (total >= 0),
estado VARCHAR(20) NOT NULL DEFAULT 'PENDIENTE'
);Analicemos el código fragmento a fragmento:
PRIMARY KEY GENERATED ALWAYS AS IDENTITY: define la clave primaria, el identificador único de cada fila. La base la genera automáticamente, así que nunca habrá dos clientes con el mismoid.UNIQUEenemail: la base garantiza que no se repita ningún correo. Es una regla de integridad que vive en los datos, no en el código de la aplicación.REFERENCES clientes(id): declara una clave foránea. La base impide insertar un pedido cuyocliente_idno exista, evitando "pedidos huérfanos". Esto se llama integridad referencial.CHECK (total >= 0): una restricción que rechaza totales negativos. De nuevo, la regla la aplica el motor.
Para recuperar los pedidos de un cliente combinamos ambas tablas con un JOIN:
SELECT c.nombre, p.id AS pedido_id, p.total FROM clientes c JOIN pedidos p ON p.cliente_id = c.id -- une filas que comparten el id WHERE c.email = '[email protected]' ORDER BY p.total DESC;
El JOIN es la operación estrella del modelo relacional: permite componer datos normalizados (sin duplicación) en el momento de la consulta. La fortaleza es la flexibilidad para hacer preguntas no previstas; el coste es que los JOIN muy grandes pueden ser lentos y difíciles de escalar horizontalmente.
- Qué significa realmente "NoSQL"
"NoSQL" es un nombre desafortunado: no significa "sin SQL" (muchas de estas bases ofrecen lenguajes de consulta parecidos) sino "Not Only SQL". Agrupa motores que renuncian total o parcialmente al modelo relacional para ganar en escalabilidad horizontal, flexibilidad de esquema o rendimiento en accesos concretos. Sus rasgos habituales:
- Esquema flexible: cada registro puede tener campos distintos sin migraciones previas.
- Escalado horizontal nativo: están diseñadas para repartir datos entre muchos nodos (sharding) desde el primer día.
- Desnormalización: en lugar de
JOIN, se duplican datos para que cada lectura toque un solo registro. - Garantías relajadas: a menudo sacrifican consistencia inmediata a cambio de disponibilidad y velocidad.
- Las cuatro familias NoSQL
No todas las bases NoSQL son iguales. Se clasifican en cuatro grandes familias según su modelo de datos:
3.1 Documental
Almacenan documentos autocontenidos, normalmente en JSON/BSON. Cada documento agrupa toda la información relacionada. Ejemplos: MongoDB, Couchbase. El mismo cliente con sus pedidos del ejemplo SQL se vería así en un único documento:
{
"_id": "cli_1042",
"nombre": "Ana López",
"email": "[email protected]",
"pedidos": [
{ "pedido_id": "ped_5001", "total": 49.90, "estado": "ENVIADO" },
{ "pedido_id": "ped_5002", "total": 12.50, "estado": "PENDIENTE" }
]
}Observa la diferencia clave frente a SQL:
- Los pedidos están embebidos dentro del cliente, en un array
pedidos. Para mostrar la ficha del cliente con sus pedidos basta una sola lectura, sinJOIN. - No hay esquema fijo: otro cliente podría no tener el campo
pedidos, o tener un campotelefonoadicional, sin que nadie tenga que alterar la "tabla". - El precio de esta comodidad es la duplicación: si el nombre de un producto cambia y está copiado en miles de documentos, hay que actualizarlos todos.
3.2 Clave-valor
El modelo más simple: un diccionario gigante que asocia una clave a un valor opaco. Ejemplos: Redis, DynamoDB (en su uso básico). Ideal para sesiones, cachés y contadores. Pseudocódigo de acceso:
Es extremadamente rápido porque la operación es un acceso directo por clave, pero no se puede consultar por el contenido del valor: solo se busca por la clave exacta.
3.3 Columnar (wide-column)
Almacenan los datos por columnas/familias de columnas en lugar de por filas, optimizadas para escrituras masivas y lecturas de rangos enormes. Ejemplos: Apache Cassandra, HBase. Brillan en series temporales y registros de eventos a escala de petabytes.
3.4 Grafo
Modelan entidades (nodos) y sus relaciones (aristas) como ciudadanos de primera clase. Ejemplos: Neo4j, Amazon Neptune. Cuando lo importante son las relaciones profundas (redes sociales, detección de fraude, recomendaciones, "amigos de amigos"), recorren conexiones de forma muchísimo más eficiente que un JOIN recursivo en SQL.
- Garantías transaccionales: ACID frente a BASE
Aquí está la diferencia conceptual más importante. Una transacción es un conjunto de operaciones que deben tratarse como una unidad.
ACID (propio del mundo relacional) garantiza:
| Letra | Propiedad | Significado |
|---|---|---|
| A | Atomicidad | Todo o nada: o se aplican todas las operaciones o ninguna. |
| C | Consistencia | La base pasa de un estado válido a otro válido (se respetan restricciones). |
| I | Aislamiento | Las transacciones concurrentes no se pisan entre sí. |
| D | Durabilidad | Una vez confirmada, sobrevive a caídas del sistema. |
Ejemplo clásico: transferir dinero entre dos cuentas debe ser atómico.
BEGIN; -- inicia la transacción UPDATE cuentas SET saldo = saldo - 100 WHERE id = 1; -- resta de la cuenta origen UPDATE cuentas SET saldo = saldo + 100 WHERE id = 2; -- suma a la cuenta destino COMMIT; -- confirma ambas a la vez
Si el sistema cae entre los dos UPDATE, la atomicidad asegura que ninguno se aplica: el dinero nunca "desaparece". Esta garantía es indispensable en banca, contabilidad o seguros.
BASE (frecuente en NoSQL distribuido) es el enfoque opuesto, pensado para sistemas que priorizan la disponibilidad a gran escala:
- Basically Available: el sistema siempre responde, aunque sea con datos algo antiguos.
- Soft state: el estado puede cambiar con el tiempo sin nuevas escrituras (por la propagación entre nodos).
- Eventually consistent: si dejan de llegar escrituras, todos los nodos acabarán convergiendo al mismo valor, pero durante un breve intervalo pueden diferir.
La elección entre ACID y BASE está ligada al teorema CAP (visto en el Módulo 4): ante una partición de red, un sistema distribuido debe elegir entre consistencia y disponibilidad. ACID tiende a privilegiar la consistencia; BASE, la disponibilidad.
- Tabla comparativa SQL vs NoSQL
| Aspecto | SQL (relacional) | NoSQL (genérico) |
|---|---|---|
| Modelo de datos | Tablas con esquema fijo | Documentos, clave-valor, columnas o grafos |
| Esquema | Rígido, definido antes | Flexible, dinámico |
| Relaciones | JOIN e integridad referencial |
Desnormalización / embebido |
| Transacciones | ACID completas | A menudo BASE / ACID limitado |
| Escalado | Principalmente vertical | Horizontal nativo |
| Lenguaje | SQL estándar | Variado por motor |
| Consultas ad-hoc | Excelente | Limitado (depende del diseño previo) |
| Mejor para | Datos estructurados con reglas fuertes | Gran volumen, alta escala, esquema cambiante |
- Criterios de elección y antipatrones
No elijas por moda. Hazte estas preguntas:
- ¿Tus datos tienen relaciones complejas y reglas de integridad fuertes? → SQL.
- ¿Necesitas consultas ad-hoc impredecibles? → SQL (los
JOINte salvan). - ¿Tienes un patrón de acceso muy conocido y un volumen enorme? → NoSQL diseñado a medida de ese patrón.
- ¿El esquema evoluciona constantemente y los datos son semiestructurados? → documental.
- ¿Lo crítico son las relaciones (recorridos profundos)? → grafo.
- ¿Necesitas accesos ultrarrápidos por clave (sesiones, caché)? → clave-valor.
Errores Comunes y Consejos
- Elegir NoSQL "porque escala" sin tener problema de escala. La mayoría de aplicaciones nunca alcanza el volumen donde el escalado horizontal compensa la pérdida de
JOINy transacciones. Empieza por SQL salvo que tengas una razón clara. - Modelar un documento como si fuera una tabla. Si en NoSQL documental acabas haciendo "JOIN a mano" en el código, has elegido mal el modelo o el diseño del documento.
- Creer que NoSQL no tiene esquema. Sí lo tiene, solo que vive en tu código en lugar de en la base. Esto traslada la responsabilidad de validación a la aplicación.
- Asumir que "eventual consistency" es inofensivo. En un saldo bancario o un stock, leer un dato obsoleto puede ser un error grave. Conoce tus requisitos de consistencia.
- Consejo: mide antes de migrar. Un índice bien puesto en SQL resuelve muchísimos problemas de rendimiento atribuidos erróneamente al "modelo relacional".
Ejercicios
Ejercicio 1. Tienes una aplicación de banca que registra movimientos de cuentas y debe garantizar que nunca se pierda ni se duplique dinero. ¿SQL o NoSQL? Justifica con dos propiedades concretas.
Ejercicio 2. Diseña, en JSON, un documento para un perfil de usuario de una red social que incluya sus datos básicos y una lista de sus tres últimas publicaciones embebidas. Indica qué problema de mantenimiento introduces al embeber las publicaciones.
Ejercicio 3. Para un catálogo de productos donde cada categoría tiene atributos completamente distintos (un libro tiene "autor", un televisor tiene "pulgadas"), ¿qué familia de base de datos encaja mejor y por qué?
Soluciones
Solución 1. SQL relacional con transacciones ACID. Razones: la atomicidad garantiza que un movimiento (cargo + abono) se aplique entero o no se aplique, y la durabilidad asegura que una vez confirmado sobrevive a una caída. Además la integridad referencial y las restricciones CHECK evitan estados inválidos como saldos negativos no permitidos.
Solución 2. Documento de ejemplo:
{
"_id": "usr_77",
"nombre": "Marc Riera",
"bio": "Aficionado a la fotografía",
"publicaciones_recientes": [
{ "id": "pub_901", "texto": "¡Buenos días!", "likes": 12 },
{ "id": "pub_900", "texto": "Foto del atardecer", "likes": 48 }
]
}Problema de mantenimiento: las publicaciones están duplicadas. Si una publicación se edita o cambia su número de likes, hay que actualizarla tanto en su colección original como dentro de cada documento de usuario que la embeba, con riesgo de incoherencias.
Solución 3. Una base documental. Su esquema flexible permite que cada producto tenga exactamente los atributos de su categoría sin columnas vacías ni tablas separadas por tipo, algo que en SQL obligaría a esquemas EAV incómodos o a muchas tablas.
Conclusión
Has aprendido que SQL y NoSQL no son rivales, sino herramientas con compromisos distintos: SQL aporta esquema fuerte, JOIN flexibles y garantías ACID; NoSQL aporta esquema flexible, escalado horizontal y, a cambio, suele relajar la consistencia (BASE). Conoces las cuatro familias NoSQL y un conjunto de criterios para decidir, recordando que muchas arquitecturas modernas adoptan persistencia políglota (usar la herramienta adecuada para cada necesidad). Una vez decidido dónde guardar los datos, la siguiente pregunta es cómo estructurar el código que accede a ellos sin ensuciar la lógica de negocio. Eso lo veremos en la próxima lección: Patrones de Acceso a Datos: Repository, Unit of Work y DAO.
Curso de Arquitectura de Aplicaciones
Módulo 1: Fundamentos de la Arquitectura de Aplicaciones
- ¿Qué es la Arquitectura de Aplicaciones?
- El Rol del Arquitecto de Software
- Atributos de Calidad y Requisitos No Funcionales
- Decisiones Arquitectónicas y Compromisos (Trade-offs)
- Documentación de Arquitectura: Vistas y el Modelo C4
Módulo 2: Principios y Tácticas de Diseño
- Acoplamiento, Cohesión y Separación de Responsabilidades
- Principios SOLID Aplicados a la Arquitectura
- DRY, KISS, YAGNI y Otros Principios de Diseño
- Tácticas Arquitectónicas para los Atributos de Calidad
- Gestión de la Deuda Técnica
Módulo 3: Estilos y Patrones Arquitectónicos
- Arquitectura Monolítica
- Arquitectura en Capas (N-Tier)
- Arquitectura Cliente-Servidor
- Arquitectura Hexagonal (Puertos y Adaptadores)
- Arquitectura Limpia y Cebolla (Clean & Onion)
Módulo 4: Arquitecturas Distribuidas y Microservicios
- Introducción a los Sistemas Distribuidos
- Arquitectura de Microservicios
- Descomposición de Servicios y Bounded Contexts
- API Gateway, Service Discovery y Comunicación entre Servicios
- Patrones de Resiliencia: Circuit Breaker, Retry y Bulkhead
- El Teorema CAP y la Consistencia de Datos
Módulo 5: Arquitecturas Dirigidas por Eventos y Mensajería
- Fundamentos de la Arquitectura Orientada a Eventos
- Mensajería Asíncrona: Colas y Brokers
- Patrones de Eventos: Event Sourcing y CQRS
- Gestión de Transacciones Distribuidas: Patrón Saga
- Streaming de Datos en Tiempo Real
Módulo 6: Diseño Dirigido por el Dominio (DDD)
- Conceptos Fundamentales del DDD
- Diseño Estratégico: Bounded Contexts y Lenguaje Ubicuo
- Diseño Táctico: Entidades, Agregados y Repositorios
- Mapeo de Contextos (Context Mapping)
Módulo 7: Datos y Persistencia
- Estrategias de Persistencia: SQL vs NoSQL
- Patrones de Acceso a Datos: Repository, Unit of Work y DAO
- Base de Datos por Servicio y Gestión de Datos Distribuidos
- Caché y Estrategias de Invalidación
Módulo 8: Arquitectura en la Nube y Despliegue
- Fundamentos del Cloud Computing (IaaS, PaaS, SaaS)
- Contenedores y Orquestación con Docker y Kubernetes
- Arquitectura Serverless
- Patrones de Diseño Cloud-Native
- Infraestructura como Código (IaC)
Módulo 9: Calidad, Seguridad y Observabilidad
- Escalabilidad: Horizontal vs Vertical y Balanceo de Carga
- Alta Disponibilidad y Tolerancia a Fallos
- Seguridad por Diseño y Autenticación/Autorización
- Observabilidad: Logging, Métricas y Trazabilidad
- Rendimiento y Pruebas de Carga
