Tags (71)
- Agile
- Alta disponibilidad
- Alternativas cloud
- Aop
- Arquitectura
- Arquitectura distribuida
- Automatizacion
- Azure devops
- Base de datos
- Buenas practicas
- Cloud
- Colas
- Competing consumers
- Convenciones
- Copilot
- Diseno
- Docker
- Docker compose
- Documentacion
- Eda
- Equipos
- Escalabilidad
- Flujo de negocio
- Flujo de trabajo
- Flyway
- Git
- Gradle
- Herramientas digitales
- Ia
- Iam
- Infraestructura
- Java
- Jerarquia tecnica
- Jpa
- Jsonb
- Kafka
- Kubernetes
- Liderazgo en software
- Lineamientos
- Log
- Logging
- Microservicios
- Mongodb
- Monitoreo
- Nosql
- Observabilidad
- Open source
- Plugins
- Postgresql
- Privacidad
- Programacion funcional
- Programacion reactiva
- Rabbitmq
- Rotacion de talento
- Saga
- Scrum
- Security
- Seguridad
- Self hosting
- Sistemas legados
- Spring boot
- Spring mvc
- Sql
- Streams
- Threadlocal
- Trazabilidad
- Versionado
- Web
- Webflux
- Websockets
- Zero trust
Explorando las Topologías de Infraestructura en la Nube: Desde el Monolito hasta la Inteligencia Artificial Distribuida
- Mauricio ECR
- DevOps
- 05 Apr, 2025
Introducción El crecimiento exponencial de las soluciones en la nube ha transformado la manera en que diseñamos, desplegamos y mantenemos nuestras aplicaciones. Ya no hablamos solo de servidores y
Explorando las Topologías de Infraestructura en la Nube: Desde el Monolito hasta la Inteligencia Artificial Distribuida
- Mauricio ECR
- DevOps
- 05 Apr, 2025
Introducción
El crecimiento exponencial de las soluciones en la nube ha transformado la manera en que diseñamos, desplegamos y mantenemos nuestras aplicaciones. Ya no hablamos solo de servidores y bases de datos, sino de arquitecturas complejas y adaptativas que responden a necesidades específicas de negocio, escalabilidad y resiliencia. En este artículo, exploramos las principales topologías de infraestructura en la nube, categorizadas según su propósito principal. Para cada subcategoría se describe de manera fluida sus componentes clave, casos de uso comunes y ejemplos concretos. Finalizamos con un cuadro resumen que compara escalabilidad, resiliencia y elasticidad entre ellas.
Categoría 1: Aplicaciones Web y Backend
• Monolito en una sola instancia
Una única unidad que contiene toda la lógica de negocio y presentación. Suele usarse cuando se prioriza velocidad de desarrollo y simplicidad.
- Componentes: Servidor de aplicaciones, base de datos, DNS, logs.
- Caso de uso: Ideal para MVPs o aplicaciones internas simples donde se necesita rapidez de desarrollo.
- Ejemplo: App de reservas de un coworking local sin alta demanda.
• Arquitectura 2-tier
Separación básica entre presentación y lógica/datos. Aumenta ligeramente la modularidad y seguridad.
- Componentes: Frontend, backend, base de datos, red privada (VPC).
- Caso de uso: Aplicaciones CRUD sin requerimientos complejos.
- Ejemplo: Sistema de inventario de una tienda de barrio.
• Arquitectura 3-tier
División clara entre presentación, lógica de negocio y almacenamiento. Facilita escalabilidad y mantenimiento.
- Componentes: Web UI, capa de negocio/API, base de datos, cache, WAF, balanceador de carga.
- Caso de uso: Aplicaciones con múltiples usuarios y procesos críticos.
- Ejemplo: Portal de empleados de una empresa con 500+ usuarios.
• Microservicios
División modular del sistema en servicios independientes. Facilita despliegue continuo y escalado específico.
- Componentes: API Gateway, servicios desacoplados, base de datos por servicio, orquestador, service mesh.
- Caso de uso: Aplicaciones con alta demanda de escalabilidad y despliegue continuo.
- Ejemplo: Plataforma de e-commerce como Amazon.
• Serverless (FaaS)
Funcionalidad sin gestión directa de servidores. Excelente para eventos aislados y costos por uso.
- Componentes: Funciones (Lambda), API Gateway, triggers, S3/DynamoDB.
- Caso de uso: Ejecuciones bajo demanda o respuestas rápidas sin infraestructura constante.
- Ejemplo: Formulario web con almacenamiento directo en DynamoDB.
• Contenerizada
Uso de contenedores para portabilidad y consistencia. Ideal para equipos DevOps maduros.
- Componentes: Contenedores, orquestador (Kubernetes/ECS), ingress, config maps, monitoreo.
- Caso de uso: Apps modernas con despliegues ágiles.
- Ejemplo: Backend de servicios para una fintech.
• Edge / CDN-based
Distribución de contenido y funciones lo más cerca posible del usuario. Reduce latencia significativamente.
- Componentes: CDN, funciones en el edge, almacenamiento estático, DNS.
- Caso de uso: Sitios globales, rápidos y ligeros.
- Ejemplo: Página de campaña para Spotify con alto tráfico mundial.
Categoría 2: Procesamiento de Datos
• Batch Processing
Ejecución por lotes en horarios programados. Óptimo para procesos no críticos en tiempo real.
- Componentes: Orquestador, Spark/Glue, almacenamiento, data warehouse.
- Caso de uso: Procesos intensivos pero no urgentes.
- Ejemplo: Generación de reportes contables semanales.
• Streaming Processing
Procesamiento en tiempo real de eventos. Permite reacciones inmediatas.
- Componentes: Kafka, Flink, almacenamiento rápido, alertas.
- Caso de uso: Monitoreo o reacción instantánea.
- Ejemplo: Sistema de detección de fraudes financieros.
• ETL / ELT
Extracción, transformación y carga de datos. Clave para flujos de datos confiables.
- Componentes: Extractores, transformadores (dbt), destino (DW), scheduler.
- Caso de uso: Consolidación de fuentes de datos.
- Ejemplo: Integración entre sistema de ventas y CRM.
• Data Warehouse
Sistema analítico estructurado. Usado para análisis de negocio y reporting.
- Componentes: Redshift, Snowflake, BigQuery, herramientas BI.
- Caso de uso: Análisis de KPIs, dashboards.
- Ejemplo: Reportes de ventas mensuales para dirección comercial.
• Data Lake / Lakehouse
Almacenamiento de grandes volúmenes en múltiples formatos. Flexible y económico.
- Componentes: S3, Glue/Athena, catálogos de datos, seguridad.
- Caso de uso: Escenarios con datos semi/no estructurados.
- Ejemplo: Logs masivos de navegación en web.
• Data Mesh
Modelo distribuido y federado de datos. Favorece la autonomía por dominio.
- Componentes: Dominios de datos, APIs, gobernanza, interoperabilidad.
- Caso de uso: Independencia de equipos para publicar/consumir datos.
- Ejemplo: Multinacional con unidades de negocio autónomas.
Categoría 3: Machine Learning / Inteligencia Artificial
• Entrenamiento simple/local
Ejecutado en entornos personales o educativos. Bajo costo y accesibilidad.
- Componentes: Notebooks (Colab, Jupyter), datasets, librerías ML.
- Caso de uso: Prototipos y aprendizaje.
- Ejemplo: Clasificador de sentimientos en Google Colab.
• Entrenamiento distribuido
Requiere potencia de cómputo alto y paralelización. Usado en proyectos avanzados.
- Componentes: Clúster de GPUs, datasets, framework (TensorFlow/PyTorch).
- Caso de uso: Modelos de lenguaje o visión a gran escala.
- Ejemplo: Modelo LLM para sector salud.
• Inferencia batch
Aplicación del modelo a conjuntos de datos no urgentes. Utilizado en análisis periódicos.
- Componentes: Modelo, job programado, input/output, auditoría.
- Caso de uso: Predicciones acumuladas.
- Ejemplo: Scoring de riesgo semanal para cartera de clientes.
• Inferencia en tiempo real
Predicciones inmediatas vía API. Fundamental para experiencias personalizadas.
- Componentes: API, modelo cargado en memoria, balanceador.
- Caso de uso: Recomendaciones o respuestas interactivas.
- Ejemplo: Recomendador de productos online.
• MLOps Pipelines
Automatización del ciclo de vida ML. Mejora confiabilidad y trazabilidad.
- Componentes: CI/CD, versionado de datos/modelos, feature store.
- Caso de uso: Iteración y despliegue continuo de modelos.
- Ejemplo: Monitoreo y redeploy de modelo de anomalías.
• AutoML
Modelado automatizado con herramientas visuales. Democratiza el uso del ML.
- Componentes: Plataforma visual, datasets, API.
- Caso de uso: Democratización del ML.
- Ejemplo: Clasificador usando Google Vertex AI.
Categoría 4: DevOps y Entornos de Desarrollo
• CI/CD básico
Automatización del flujo de despliegue. Mejora velocidad y calidad.
- Componentes: Repositorio, pipelines, build/test/deploy.
- Caso de uso: Flujos simples de desarrollo continuo.
- Ejemplo: GitHub Actions para app Node.js.
• Entornos efímeros
Instancias temporales por cambios en código. Perfecto para validación rápida.
- Componentes: IaC, previsualizaciones por rama o PR.
- Caso de uso: Validación visual antes de merge.
- Ejemplo: Landing page preview con Vercel.
• Entornos dedicados
Separación de ambientes según etapa de desarrollo. Facilita pruebas seguras.
- Componentes: Dev, QA, Staging, Prod, variables.
- Caso de uso: Flujo robusto de pruebas.
- Ejemplo: Pipeline de pagos con pruebas por entorno.
• Infraestructura como Código (IaC)
Infra reproducible y versionada. Ideal para automatización multicloud.
- Componentes: Terraform/Pulumi, módulos, backends de estado.
- Caso de uso: Gestión multicloud declarativa.
- Ejemplo: Infraestructura de microservicios con Terraform.
• DevSecOps
Seguridad integrada al ciclo de desarrollo. Reduce riesgos desde el origen.
- Componentes: SAST, control de secretos, validación de seguridad.
- Caso de uso: Garantizar seguridad desde el código.
- Ejemplo: Validación de secretos y código con Vault y SonarQube.
Resumen General
A continuación, un resumen de las topologías abordadas, con sus propiedades clave:
| Grupo | Subcategoría | Escalabilidad | Resiliencia | Elasticidad | Componentes Principales | Caso de Uso / Ejemplo |
|---|---|---|---|---|---|---|
| Web/Backend | Monolito | Baja | Baja | Nula | Servidor, BD, DNS, logs | App reservas coworking |
| Web/Backend | 2-tier | Media | Media | Baja | Frontend, backend, BD | Inventario de tienda |
| Web/Backend | 3-tier | Alta | Alta | Media | UI, API, BD, cache, WAF | Portal empleados |
| Web/Backend | Microservicios | Muy Alta | Muy Alta | Alta | API GW, servicios, BD, mesh | E-commerce estilo Amazon |
| Web/Backend | Serverless | Muy Alta | Alta | Muy Alta | Lambda, S3, triggers | Formulario Lambda |
| Web/Backend | Contenerizada | Alta | Alta | Alta | K8s/ECS, contenedores | Backend fintech |
| Web/Backend | Edge/CDN | Alta | Alta | Alta | CDN, edge functions | Landing global Spotify |
| Datos | Batch | Media | Alta | Baja | Orquestador, Glue, DW | Reportes contables |
| Datos | Streaming | Alta | Alta | Alta | Kafka, Flink, alertas | Fraudes bancarios |
| Datos | ETL/ELT | Alta | Media | Media | Extractores, DBT, DW | Datos ventas y CRM |
| Datos | DW | Alta | Alta | Media | Redshift, BI | KPI dirección comercial |
| Datos | Lakehouse | Muy Alta | Alta | Alta | S3, Athena, catálogo | Logs web |
| Datos | Data Mesh | Alta | Alta | Media | APIs, dominios, gov | Datos en multinacional |
| IA/ML | Entrenamiento local | Baja | Baja | Nula | Notebooks, datasets | Clasificador Colab |
| IA/ML | Entrenamiento distribuido | Alta | Alta | Media | GPUs, TF/PT, dataset | LLM salud |
| IA/ML | Inferencia batch | Media | Alta | Baja | Job, modelo, IO | Scoring clientes |
| IA/ML | Inferencia tiempo real | Alta | Alta | Alta | API, modelo, load balancer | Recomendador productos |
| IA/ML | MLOps | Alta | Alta | Alta | CI/CD, feature store | Modelo de anomalías |
| IA/ML | AutoML | Media | Alta | Alta | Vertex AI, API | Vertex AI |
| DevOps | CI/CD | Media | Media | Media | GitHub, pipelines | GitHub Actions |
| DevOps | Entornos efímeros | Alta | Media | Alta | IaC, previews | Preview en Vercel |
| DevOps | Entornos dedicados | Media | Alta | Baja | QA, Staging, Prod | Validación por ambiente |
| DevOps | IaC | Alta | Alta | Alta | Terraform, módulos | Terraform multicloud |
| DevOps | DevSecOps | Alta | Alta | Alta | SAST, secretos, Vault | Pipeline seguro + Vault |
Este análisis puede servirte como hoja de ruta para diseñar arquitecturas eficientes, seguras y adaptadas al crecimiento de tu organización. Cada topología tiene su momento ideal de uso, y comprenderlas te permite tomar decisiones informadas en tu estrategia de nube.
Conclusión: Hacia una Elección Informada
Elegir la topología de infraestructura adecuada en la nube no es solo una cuestión técnica, sino una decisión estratégica que impacta directamente en la eficiencia, escalabilidad y competitividad de una organización. Como hemos visto, cada enfoque —desde un monolito sencillo hasta una arquitectura de MLOps distribuida— tiene fortalezas y compromisos que deben alinearse con el contexto del negocio, la madurez tecnológica del equipo y los objetivos a corto y largo plazo.
Al evaluar cuál topología adoptar, considera las siguientes preguntas clave:
- ¿Cuál es la etapa de madurez de tu producto o servicio?
- ¿Qué tan crítico es el tiempo de respuesta o la resiliencia para tu operación?
- ¿Tu equipo está preparado para gestionar la complejidad que implica escalar?
- ¿Qué tanto valor aportaría la automatización y observabilidad en tu flujo de trabajo?
Comenzar con una solución simple y evolucionar progresivamente hacia arquitecturas más sofisticadas es una estrategia válida y, en muchos casos, recomendable. Lo esencial es tener claridad en el propósito y una visión arquitectónica que te permita crecer sin reescribir desde cero cada vez que el negocio escale.
En definitiva, la nube no es solo infraestructura: es una plataforma para innovar con agilidad. Entender sus topologías es el primer paso para diseñar soluciones más robustas, eficientes y orientadas al futuro.
Gitea: Cómo Montar tu Propio Servidor Git en Minutos
- Mauricio ECR
- DevOps
- 04 Apr, 2025
A medida que los proyectos crecen y se diversifican, muchos desarrolladores empiezan a preguntarse si realmente necesitan depender de plataformas como GitHub o GitLab para gestionar su código. No es q
Gitea: Cómo Montar tu Propio Servidor Git en Minutos
- Mauricio ECR
- DevOps
- 04 Apr, 2025
A medida que los proyectos crecen y se diversifican, muchos desarrolladores empiezan a preguntarse si realmente necesitan depender de plataformas como GitHub o GitLab para gestionar su código. No es que esas herramientas estén mal, pero hay escenarios donde una alternativa más simple y autoalojada tiene mucho sentido.
Este artículo no es para convencerte de abandonar nada, sino para mostrarte una opción: Gitea, una plataforma ligera y fácil de montar para tener tu propio servidor Git.
¿Por Qué Considerar Gitea?
Si trabajas en proyectos personales, con un equipo pequeño, o simplemente te interesa aprender a montar tu propia infraestructura, hay algunas razones por las que podrías querer probar Gitea:
- Autonomía: No dependes de servidores externos.
- Simplicidad: La instalación es directa, sin configuraciones complejas.
- Control: Tú decides cómo se almacenan y gestionan los datos.
No es una solución perfecta para todos los casos, pero vale la pena conocerla. A veces lo que uno necesita es justamente eso: algo sencillo que funcione.
Cómo Instalar Gitea con Docker
Una de las formas más rápidas de poner Gitea en marcha es con Docker. Aquí va una guía paso a paso, sin adornos.
1. Crea un directorio para el proyecto
mkdir gitea && cd gitea
2. Crea un archivo docker-compose.yml
version: "3"
services:
server:
image: gitea/gitea:latest
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
restart: unless-stopped
volumes:
- ./data:/data
ports:
- "3000:3000" # Web
- "2222:22" # SSH
3. Levanta el contenedor
docker-compose up -d
4. Abre el navegador
Accede a http://localhost:3000 y completa la configuración inicial. No toma más de un par de minutos.
Subir un Repositorio a Gitea
Una vez que el servidor está funcionando, puedes empezar a usarlo como cualquier otro servicio Git.
- Crea un nuevo repositorio desde la interfaz web de Gitea.
- En tu máquina local, añade ese repositorio como remoto:
git remote add gitea http://localhost:3000/usuario/repositorio.git
git push -u gitea main
Y eso es todo. Estás trabajando con Git como siempre, pero ahora en tu propio servidor.
🔐 Autenticación por SSH
Subir y clonar repositorios por HTTP está bien para empezar, pero si planeas trabajar frecuentemente con tu servidor Gitea, usar SSH es mucho más seguro y cómodo.
1. Genera tu clave SSH (si no tienes una)
ssh-keygen -t ed25519 -C "[email protected]"
Esto creará dos archivos en ~/.ssh: una clave privada (id_ed25519) y una pública (id_ed25519.pub).
2. Agrega tu clave pública a Gitea
- En la interfaz web de Gitea, ve a "Tu Perfil" > "SSH Keys"
- Pega el contenido de
~/.ssh/id_ed25519.pub
3. Usa el remoto por SSH
Ahora puedes clonar así:
git clone ssh://git@localhost:2222/usuario/repositorio.git
Y también hacer push/pull sin tener que escribir tu contraseña.
⚙️ Integración de CI/CD con Drone
Aunque Gitea no incluye un sistema de CI/CD por defecto, es compatible con herramientas externas como Drone CI. Vamos a ver cómo integrarlo.
Docker Compose con Gitea + Drone
version: '3'
services:
gitea:
image: gitea/gitea:latest
container_name: gitea
restart: always
environment:
- USER_UID=1000
- USER_GID=1000
volumes:
- ./gitea:/data
ports:
- "3000:3000"
- "2222:22"
networks:
- cicd-net
drone:
image: drone/drone:latest
container_name: drone
restart: always
ports:
- "8080:80"
volumes:
- ./drone:/data
environment:
- DRONE_GITEA_SERVER=http://gitea:3000
- DRONE_GITEA_CLIENT_ID=tu-client-id
- DRONE_GITEA_CLIENT_SECRET=tu-client-secret
- DRONE_RPC_SECRET=una-clave-secreta
- DRONE_SERVER_HOST=localhost:8080
- DRONE_SERVER_PROTO=http
- DRONE_USER_CREATE=username:tu-usuario,admin:true
networks:
- cicd-net
networks:
cicd-net:
driver: bridge
Pasos Adicionales
Registra una aplicación OAuth en Gitea desde
http://localhost:3000/user/settings/applications.Usa
http://localhost:8080/logincomo Redirect URI.Copia el
Client IDyClient Secreten eldocker-compose.yml.Levanta los servicios:
docker-compose up -d
- Accede a Drone en
http://localhost:8080y conecta tu cuenta de Gitea. - Agrega un archivo
.drone.ymlen tu repositorio:
kind: pipeline
type: docker
name: default
steps:
- name: test
image: node:18
commands:
- npm install
- npm test
- Activa el repositorio desde la interfaz de Drone.
🧭 Reflexión Final
Gitea es más que una curiosidad para quienes prefieren el control. Es una herramienta real, funcional y mantenida activamente. No está diseñada para reemplazar gigantes, pero sí para darte una alternativa cuando quieres aprender, experimentar o simplemente no depender de nadie más.
Con unos pocos comandos puedes levantar tu propio servidor Git, usar autenticación SSH, y hasta conectar una pipeline de CI/CD.
Y eso, en ciertos contextos, es todo lo que necesitas.
WebSockets Seguros en Spring Boot: Protege tus Conexionesen Tiempo Real
- Mauricio ECR
- Seguridad
- 04 Apr, 2025
Introducción En la era de las aplicaciones en tiempo real, los WebSockets se han convertido en una tecnología fundamental para crear experiencias interactivas. Sin embargo, su naturaleza persisten
WebSockets Seguros en Spring Boot: Protege tus Conexionesen Tiempo Real
- Mauricio ECR
- Seguridad
- 04 Apr, 2025
Introducción
En la era de las aplicaciones en tiempo real, los WebSockets se han convertido en una tecnología fundamental para crear experiencias interactivas. Sin embargo, su naturaleza persistente y bidireccional presenta desafíos únicos de seguridad.
¿Cómo garantizar que solo usuarios autenticados puedan establecer conexiones WebSocket?
¿Cómo proteger los mensajes intercambiados?
Este artículo presenta una implementación completa de WebSockets seguros en Spring Boot, con explicaciones detalladas de cada componente y su función en el sistema de seguridad.
🛠️ Implementación del Backend
1. Clase Principal de la Aplicación
Esta es la clase de entrada estándar para una aplicación Spring Boot, que inicia el contexto de la aplicación.
package com.mecr.sample.webSocketsSecurity;
@SpringBootApplication
public class WebSocketsSecurityApplication {
public static void main(String[] args) {
SpringApplication.run(WebSocketsSecurityApplication.class, args);
}
}
2. Configuración de Seguridad
La clase SecurityConfig define las reglas de seguridad para la aplicación, incluyendo la protección de endpoints WebSocket, configuración CORS y autenticación básica.
package com.mecr.sample.webSocketsSecurity.security.config;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/ws/**").authenticated()
.anyRequest().permitAll()
)
.formLogin(withDefaults())
.logout(withDefaults());
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("http://127.0.0.1:5500", "http://localhost:5500"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
config.setAllowCredentials(true);
config.setAllowedHeaders(List.of("Authorization", "Content-Type", "X-Requested-With"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("admin")
.password("admin")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
3. Interceptor de Autenticación para WebSockets
El AuthHandshakeInterceptor verifica la autenticación del usuario antes de permitir el establecimiento de la conexión WebSocket.
package com.mecr.sample.webSocketsSecurity.webSocket.security;
public class AuthHandshakeInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Map<String, Object> attributes) {
if (request instanceof ServletServerHttpRequest servletRequest) {
HttpSession session = servletRequest.getServletRequest().getSession(false);
if (session != null) {
SecurityContext context = (SecurityContext) session.getAttribute("SPRING_SECURITY_CONTEXT");
if (context != null && context.getAuthentication() != null && context.getAuthentication().isAuthenticated()) {
attributes.put("AUTH", context.getAuthentication());
System.out.println("🔐 Usuario autenticado en interceptor: " + context.getAuthentication().getName());
return true;
}
}
}
System.out.println("❌ WebSocket rechazado en interceptor (no autenticado)");
return false;
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Exception exception) {
}
}
4. Manejador de WebSockets
MyWebSocketHandler gestiona los eventos del ciclo de vida de la conexión WebSocket y el procesamiento de mensajes.
package com.mecr.sample.webSocketsSecurity.webSocket.handler;
@Component
public class MyWebSocketHandler extends TextWebSocketHandler {
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
Authentication authentication = (Authentication) session.getAttributes().get("AUTH");
if (authentication == null || !authentication.isAuthenticated()) {
System.out.println("❌ WebSocket rechazado: Usuario no autenticado");
session.close(CloseStatus.NOT_ACCEPTABLE);
return;
}
System.out.println("✅ WebSocket conectado: " + authentication.getName());
session.sendMessage(new TextMessage("Conexión exitosa! Bienvenido " + authentication.getName()));
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
Authentication authentication = (Authentication) session.getAttributes().get("AUTH");
if (authentication == null || !authentication.isAuthenticated()) {
session.close(CloseStatus.NOT_ACCEPTABLE);
return;
}
System.out.println("📩 Mensaje recibido de " + authentication.getName() + ": " + message.getPayload());
session.sendMessage(new TextMessage("Echo: " + message.getPayload()));
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
System.err.println("❌ Error en WebSocket: " + exception.getMessage());
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
System.out.println("🚪 WebSocket cerrado");
}
}
5. Configuración de WebSockets
WebSocketConfig registra el manejador WebSocket y configura los interceptores necesarios.
package com.mecr.sample.webSocketsSecurity.webSocket.config;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
private final MyWebSocketHandler myWebSocketHandler;
public WebSocketConfig(MyWebSocketHandler myWebSocketHandler) {
this.myWebSocketHandler = myWebSocketHandler;
}
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myWebSocketHandler, "/ws")
.setAllowedOrigins("http://localhost:5500","http://127.0.0.1:5500")
.addInterceptors(new HttpSessionHandshakeInterceptor(), new AuthHandshakeInterceptor());
}
}
💻 Implementación del Frontend
Esta página HTML demuestra cómo interactuar con el backend seguro desde el navegador.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>WebSocket con Seguridad</title>
</head>
<body>
<h2>WebSocket Seguro</h2>
<button onclick="login()">Iniciar Sesión</button>
<button onclick="connectWebSocket()">Conectar WebSocket</button>
<button onclick="sendMessage()">Enviar Mensaje</button>
<p id="output"></p>
<script>
var socket;
function login() {
fetch("http://127.0.0.1:8080/login", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: "username=admin&password=admin",
credentials: "include"
}).then(response => {
if (response.ok) {
alert("Autenticado correctamente");
} else {
alert("Error de autenticación");
}
}).catch(error => console.error("Error:", error));
}
function connectWebSocket() {
socket = new WebSocket("ws://127.0.0.1:8080/ws");
socket.onopen = function() {
console.log("✅ WebSocket conectado!");
socket.send("Hola servidor!");
};
socket.onmessage = function(event) {
document.getElementById("output").innerText = "Respuesta del servidor: " + event.data;
};
socket.onerror = function(error) {
console.error("❌ Error en WebSocket:", error);
};
socket.onclose = function() {
console.log("🚪 WebSocket desconectado");
};
}
function sendMessage() {
if (socket) {
socket.send("Hola desde cliente!");
} else {
alert("Conéctate primero!");
}
}
</script>
</body>
</html>
✅ Conclusión
Esta implementación proporciona una base sólida para aplicaciones que requieren WebSockets seguros en Spring Boot. La combinación de Spring Security con interceptores personalizados garantiza que solo usuarios autenticados puedan establecer conexiones WebSocket, mientras que la configuración de CORS protege contra solicitudes no autorizadas desde otros dominios.
Los componentes clave trabajan juntos para ofrecer:
- Autenticación previa al handshake WebSocket
- Mantenimiento del contexto de seguridad durante la sesión
- Protección contra CSRF (aunque desactivada para WebSockets)
- Configuración CORS segura
- Manejo adecuado de errores y cierre de conexiones
Explorando las Bases de Datos NoSQL: Introducción a MongoDB
- Mauricio ECR
- Persistencia
- 30 Mar, 2025
📚 Introducción En un mundo donde el volumen y la variedad de los datos crecen exponencialmente, las bases de datos NoSQL se han convertido en una alternativa esencial frente a las tradicionales
Explorando las Bases de Datos NoSQL: Introducción a MongoDB
- Mauricio ECR
- Persistencia
- 30 Mar, 2025
📚 Introducción
En un mundo donde el volumen y la variedad de los datos crecen exponencialmente, las bases de datos NoSQL se han convertido en una alternativa esencial frente a las tradicionales bases de datos relacionales. Desde aplicaciones web modernas hasta análisis masivos de datos, las soluciones NoSQL ofrecen flexibilidad, escalabilidad y eficiencia en el manejo de datos estructurados y no estructurados.
En este documento, nos centraremos en las bases de datos documentales, específicamente en MongoDB. Exploraremos sus características principales, el modelo de almacenamiento basado en documentos y las operaciones básicas de manejo de datos (CRUD). Además, hablaremos brevemente sobre conceptos como el escalamiento horizontal y vertical. ¡Acompáñanos a descubrir cómo MongoDB facilita la gestión de datos en el mundo digital!.
TipoDB
Las bases de datos NoSQL se clasifican según su modelo de datos. Las principales categorías son:
1. Base de Datos Documentales
Almacenan datos en formato de documentos (generalmente JSON o BSON). Son flexibles y escalables.
MongoDB
- Características:
- Orientada a documentos.
- Alta disponibilidad mediante réplicas.
- Escalabilidad horizontal con sharding.
- Uso común: Aplicaciones web modernas, análisis de datos.
Cloud Firestore
- Características:
- Propiedad de Google Cloud.
- Basada en documentos jerárquicos.
- Integración nativa con Firebase.
- Uso común: Desarrollo móvil y web en tiempo real.
2. Grafos
Almacenan datos en nodos y relaciones entre ellos. Son ideales para consultas complejas y redes.
Neo4j
- Características:
- Modelo basado en grafos.
- Consultas eficientes con Cypher Query Language.
- Ideal para redes sociales, recomendaciones y fraudes.
- Uso común: Análisis de redes, sistemas de recomendación.
3. Clave-Valor
Almacenan datos como pares clave-valor. Son simples y extremadamente rápidas.
Redis
- Características:
- Almacenamiento en memoria (in-memory).
- Soporta estructuras de datos como listas, conjuntos y hashes.
- Persistencia opcional.
- Uso común: Caching, colas de mensajes, sesiones de usuario.
4. Columnas
Almacenan datos en columnas en lugar de filas. Son eficientes para grandes volúmenes de datos.
Cassandra
- Características:
- Sin punto único de fallo.
- Escalabilidad horizontal.
- Modelo de consistencia ajustable.
- Uso común: IoT, análisis de datos masivos.
HBase
- Características:
- Construida sobre Hadoop.
- Alta disponibilidad y tolerancia a fallos.
- Ideal para big data.
- Uso común: Procesamiento de grandes volúmenes de datos.
Escalamiento
Vertical
- Descripción: Aumentar la capacidad de un solo servidor (CPU, RAM, almacenamiento).
- Ventajas: Fácil de implementar.
- Desventajas: Limitado por el hardware disponible.
Horizontal
- Descripción: Agregar más servidores para distribuir la carga.
- Ventajas: Altamente escalable.
- Desventajas: Mayor complejidad en la gestión.
Base de Datos Documentales
Estructura
- Base de datos: Contenedor principal de datos.
- Colecciones: Grupos de documentos relacionados.
- Documentos: Unidades individuales de datos (JSON/BSON).
Implementación
- On-premises: Instalación local.
- Mongo Compass: Interfaz gráfica para MongoDB.
- Cluster: Conjunto de servidores MongoDB trabajando juntos.
Tipos de Datos
- JSON: Formato ligero para intercambio de datos.
- BSON: Extensión binaria de JSON usada internamente por MongoDB.
CRUD (Create, Read, Update, Delete)
| Operación | Descripción | Ejemplo |
|---|---|---|
insertOne |
Inserta un solo documento. | db.collection.insertOne({ name: "Alice" }) |
insertMany |
Inserta múltiples documentos. | db.collection.insertMany([{ name: "Bob" }, { name: "Charlie" }]) |
updateOne |
Actualiza un solo documento. | db.collection.updateOne({ name: "Alice" }, { $set: { age: 25 } }) |
updateMany |
Actualiza múltiples documentos. | db.collection.updateMany({}, { $inc: { age: 1 } }) |
deleteOne |
Elimina un solo documento. | db.collection.deleteOne({ name: "Alice" }) |
deleteMany |
Elimina múltiples documentos. | db.collection.deleteMany({ age: { $gt: 30 } }) |
drop |
Elimina una colección completa. | db.collection.drop() |
find |
Busca documentos que coincidan con un filtro. | db.collection.find({ age: { $gt: 20 } }) |
aggregate |
Realiza operaciones avanzadas como agrupaciones. | db.collection.aggregate([{ $group: { _id: "$age", count: { $sum: 1 } } }]) |
sort, limit, skip |
Ordena, limita y omite documentos en una consulta. | db.collection.find().sort({ age: 1 }).limit(10).skip(5) |
Operadores
| Operador | Descripción | Ejemplo |
|---|---|---|
$set |
Establece el valor de un campo. | db.collection.updateOne({ name: "Alice" }, { $set: { age: 25 } }) |
$inc |
Incrementa el valor de un campo numérico. | db.collection.updateOne({ name: "Alice" }, { $inc: { age: 1 } }) |
$rename |
Cambia el nombre de un campo. | db.collection.updateOne({}, { $rename: { oldField: "newField" } }) |
$unset |
Elimina un campo. | db.collection.updateOne({}, { $unset: { age: "" } }) |
$push |
Agrega un elemento a un array. | db.collection.updateOne({}, { $push: { hobbies: "reading" } }) |
$pull |
Elimina un elemento de un array. | db.collection.updateOne({}, { $pull: { hobbies: "reading" } }) |
$in |
Coincide si el campo tiene uno de los valores especificados. | db.collection.find({ age: { $in: [20, 25, 30] } }) |
$nin |
Coincide si el campo no tiene ninguno de los valores especificados. | db.collection.find({ age: { $nin: [20, 25, 30] } }) |
$all |
Coincide si el array contiene todos los valores especificados. | db.collection.find({ hobbies: { $all: ["reading", "writing"] } }) |
$size |
Coincide si el array tiene un tamaño específico. | db.collection.find({ hobbies: { $size: 3 } }) |
$elemMatch |
Coincide si al menos un elemento del array cumple todas las condiciones. | db.collection.find({ scores: { $elemMatch: { $gt: 80, $lt: 90 } } }) |
$regex |
Coincide con patrones de texto. | db.collection.find({ name: { $regex: "^A" } }) |
$eq y $ne |
Igualdad y desigualdad. | db.collection.find({ age: { $eq: 25 } }) |
$gt, $gte |
Mayor que y mayor o igual que. | db.collection.find({ age: { $gt: 20 } }) |
Operadores Lógicos
| Operador | Descripción | Ejemplo |
|---|---|---|
$and |
Coincide si todas las condiciones son verdaderas. | db.collection.find({ $and: [{ age: { $gt: 20 } }, { age: { $lt: 30 } }] }) |
$or |
Coincide si al menos una condición es verdadera. | db.collection.find({ $or: [{ age: { $gt: 20 } }, { name: "Alice" }] }) |
$not |
Niega una expresión. | db.collection.find({ age: { $not: { $gt: 20 } } }) |
$nor |
Coincide si ninguna de las condiciones es verdadera. | db.collection.find({ $nor: [{ age: { $gt: 20 } }, { name: "Alice" }] }) |
Aggregation Framework
Permite realizar transformaciones y análisis complejos en los datos.
Ejemplo de Pipeline
db.collection.aggregate([
{ $match: { age: { $gt: 20 } } }, // Filtra documentos
{ $group: { _id: "$city", total: { $sum: 1 } } }, // Agrupa por ciudad
{ $sort: { total: -1 } } // Ordena por total descendente
]);
📝 Conclusión
Las bases de datos NoSQL representan una evolución en la gestión de datos que responde a las necesidades actuales de escalabilidad y flexibilidad. Comprender su arquitectura, funcionamiento y aplicaciones prácticas es fundamental para cualquier profesional del ámbito tecnológico.
Si deseas profundizar aún más, explora casos de uso específicos y realiza experimentos prácticos con plataformas como MongoDB, Redis o Neo4j. La adopción y el dominio de estas tecnologías pueden marcar la diferencia en proyectos que manejan grandes volúmenes de datos o requieren consultas complejas en tiempo real. ¡Atrévete a sumergirte en el fascinante mundo NoSQL y lleva tus habilidades de manejo de datos al siguiente nivel!
Transformando Colecciones con Java Streams: 15 Métodos Esenciales
- Mauricio ECR
- Arquitectura
- 29 Mar, 2025
Introducción En el mundo de Java, trabajar con colecciones de datos solía ser sinónimo de bucles interminables, condicionales anidados y código repetitivo. Pero con la llegada de Java Streams
Transformando Colecciones con Java Streams: 15 Métodos Esenciales
- Mauricio ECR
- Arquitectura
- 29 Mar, 2025
Introducción
En el mundo de Java, trabajar con colecciones de datos solía ser sinónimo de bucles interminables, condicionales anidados y código repetitivo. Pero con la llegada de Java Streams (desde Java 8), todo cambió. Los Streams introdujeron un paradigma funcional y declarativo que permite manipular datos de manera eficiente, legible y elegante.
¿Imaginas poder filtrar, transformar, agrupar o reducir elementos con solo unas líneas de código? Los métodos de los Streams hacen esto posible, convirtiendo operaciones complejas en secuencias intuitivas. Pero para aprovecharlos al máximo, es clave conocer sus herramientas principales.
Aquí te presentamos un listado detallado de los métodos más poderosos de los Streams, divididos en dos categorías: métodos generales y métodos de agrupación. Descubre cómo dominarlos puede simplificar tu código, potenciar tu productividad y desbloquear nuevas posibilidades en el manejo de datos.
Listado de Métodos de Java Streams
Métodos Generales
- filter(Predicate<? super T> predicate)
Filtra los elementos que cumplen con una condición.List<Integer> numeros = Arrays.asList(5, 12, 3, 20); List<Integer> mayoresA10 = numeros.stream() .filter(x -> x > 10) .collect(Collectors.toList()); // Resultado: [12, 20] - map(Function<? super T, ? extends R> mapper)
Transforma cada elemento aplicando una función.List<String> palabras = Arrays.asList("java", "streams"); List<Integer> longitudes = palabras.stream() .map(String::length) .collect(Collectors.toList()); // Resultado: [4, 7] - flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
Aplana múltiples Streams en uno solo (útil para listas anidadas).List<List<Integer>> listaAnidada = Arrays.asList( Arrays.asList(1, 2), Arrays.asList(3, 4) ); List<Integer> listaPlana = listaAnidada.stream() .flatMap(List::stream) .collect(Collectors.toList()); // Resultado: [1, 2, 3, 4] - reduce(BinaryOperator
accumulator)
Reduce los elementos a un único valor mediante una operación (ej: suma).List<Integer> numeros = Arrays.asList(1, 2, 3, 4); Optional<Integer> suma = numeros.stream() .reduce((a, b) -> a + b); // Resultado: 10 - collect(Collector<? super T, A, R> collector)
Transforma el Stream en una colección o estructura de datos.List<String> palabras = Arrays.asList("a", "b", "c"); Set<String> set = palabras.stream() .collect(Collectors.toSet()); // Resultado: [a, b, c] (como Set) - forEach(Consumer<? super T> action)
Ejecuta una acción en cada elemento (como imprimirlo).List<String> frutas = Arrays.asList("Manzana", "Pera"); frutas.stream() .forEach(fruta -> System.out.print(fruta + " ")); // Resultado: "Manzana Pera " - sorted()
Ordena los elementos (requiere que sean comparables).List<Integer> numeros = Arrays.asList(3, 1, 4, 2); List<Integer> ordenados = numeros.stream() .sorted() .collect(Collectors.toList()); // Resultado: [1, 2, 3, 4] - distinct()
Elimina duplicados, retornando elementos únicos.List<Integer> numeros = Arrays.asList(2, 2, 5, 5); List<Integer> unicos = numeros.stream() .distinct() .collect(Collectors.toList()); // Resultado: [2, 5] - limit(long maxSize)
Limita el Stream a un número máximo de elementos.List<Integer> numeros = Arrays.asList(1, 2, 3, 4, 5); List<Integer> primeros3 = numeros.stream() .limit(3) .collect(Collectors.toList()); // Resultado: [1, 2, 3] - skip(long n)
Omite los primeros n elementos del Stream.List<Integer> numeros = Arrays.asList(1, 2, 3, 4, 5); List<Integer> sinPrimeros2 = numeros.stream() .skip(2) .collect(Collectors.toList()); // Resultado: [3, 4, 5]
Métodos para Agrupar Elementos
- Collectors.groupingBy(Function<? super T, ? extends K> classifier)
Agrupa elementos por una clave (ej: edad de una persona).List<Persona> personas = Arrays.asList( new Persona("Ana", 25), new Persona("Luis", 25) ); Map<Integer, List<Persona>> porEdad = personas.stream() .collect(Collectors.groupingBy(Persona::getEdad)); // Resultado: {25=[Ana, Luis]} - Collectors.partitioningBy(Predicate<? super T> predicate)
Divide el Stream en dos grupos: los que cumplen y no cumplen un predicado.// Agrupar por categoría + stock mayor a 5 Map<String, List<Producto>> porCategoriaYStock = productos.stream() .collect(Collectors.groupingBy(p -> p.getCategoria() + "-" + (p.getStock() > 5 ? "AltoStock" : "BajoStock") )); /* Resultado: { "Electrónica-AltoStock": [Laptop, Smartphone], "Ropa-AltoStock": [Camisa] } */ - Collectors.groupingBy(classifier, downstream)
Agrupa y luego aplica un segundo colector a cada grupo (ej: contar elementos).// Precio promedio por categoría Map<String, Double> precioPromedio = productos.stream() .collect(Collectors.groupingBy( Producto::getCategoria, Collectors.averagingDouble(Producto::getPrecio) )); /* Resultado: { "Electrónica": 1000.0, "Ropa": 40.0 } */ - Collectors.groupingBy(classifier, mapFactory, downstream)
Agrupa usando un tipo de mapa específico (ej: TreeMap).// Agrupar por rangos de precios Map<String, List<Producto>> porRangoPrecio = productos.stream() .collect(Collectors.groupingBy(p -> { if (p.getPrecio() < 100) return "Económico"; else if (p.getPrecio() < 1000) return "Medio"; else return "Premium"; })); /* Resultado: { "Premium": [Laptop], "Medio": [Smartphone], "Económico": [Camisa] } */ - Agrupar con downstream complejo (ej: contar y sumar stock)
// Por categoría: cantidad de productos y stock total Map<String, Map<String, Object>> estadisticas = productos.stream() .collect(Collectors.groupingBy( Producto::getCategoria, Collectors.collectingAndThen( Collectors.toList(), lista -> { int cantidad = lista.size(); int stockTotal = lista.stream().mapToInt(Producto::getStock).sum(); return Map.of("Cantidad", cantidad, "Stock Total", stockTotal); } ) )); /* Resultado: { "Electrónica": {"Cantidad": 2, "Stock Total": 15}, "Ropa": {"Cantidad": 1, "Stock Total": 20} } */
Conclusión
Dominar los métodos de Java Streams no solo simplifica tu código, sino que también mejora su legibilidad y eficiencia. Al explorar y aplicar estos métodos, descubrirás nuevas formas de manipular colecciones de datos que pueden transformar tu enfoque en el desarrollo de software. ¡Sigue profundizando y experimentando con Java Streams para desbloquear todo su potencial!
¿SQL o NoSQL? Descubre la Base de Datos Ideal para tu Proyecto
- Mauricio ECR
- Persistencia
- 29 Mar, 2025
Introducción Elegir la base de datos adecuada para un proyecto es una decisión crítica que afecta la escalabilidad, el rendimiento y la facilidad de mantenimiento de una aplicación. ¿Necesitas una
¿SQL o NoSQL? Descubre la Base de Datos Ideal para tu Proyecto
- Mauricio ECR
- Persistencia
- 29 Mar, 2025
Introducción
Elegir la base de datos adecuada para un proyecto es una decisión crítica que afecta la escalabilidad, el rendimiento y la facilidad de mantenimiento de una aplicación. ¿Necesitas una base de datos relacional o una documental? Este cuestionario te ayudará a tomar la mejor decisión basada en los requisitos específicos de tu proyecto. Responde las siguientes preguntas y obtén una recomendación basada en tus necesidades técnicas y operativas.
Contexto del Proyecto
Antes de responder, defina:
- Caso de uso principal: Ej: sistema transaccional, catálogo de productos, IoT, contenido generado por usuarios.
- Velocidad de crecimiento de datos: Estimación anual (GB/TB).
- Ratio lecturas/escrituras: Ej: 80/20, 50/50.
1. Modelado de Datos
¿Los datos tienen una estructura fija y predefinida que se mantiene estable (>80% de los casos)?
- Ej: tablas de clientes con campos obligatorios vs. posts de redes sociales con metadatos variables.
¿Es crítico modelar relaciones muchos-a-muchos entre entidades principales?
- Ej: estudiantes-cursos vs. tags en un blog.
¿La normalización para evitar redundancia es prioritaria sobre la velocidad de lectura?
¿El esquema cambia menos de 2 veces/año?
¿Los registros comparten >90% de atributos comunes?
¿Los datos son principalmente planos o con anidamiento simple (≤2 niveles)?
- Ej: dirección
{calle, ciudad}vs. JSON con subdocumentos jerárquicos.
- Ej: dirección
¿La integridad referencial (FKs) es no negociable para el negocio?
¿Los datos son >70% valores escalares (números, textos cortos) vs. documentos/blobs?
¿Se pueden representar sin pérdida en tablas 2D?
- Ej: evita estructuras como arrays o árboles.
¿Prefiere almacenar documentos completos (JSON/XML) en lugar de desnormalizar?
¿Necesita consultar fragmentos específicos dentro de documentos anidados frecuentemente?
¿Los atributos varían significativamente entre registros de la misma entidad?
- Ej: productos con especificaciones técnicas heterogéneas.
2. Operaciones y Consultas
¿Las consultas frecuentes (≥30%) requieren JOINs entre ≥3 tablas?
¿Las búsquedas acceden a campos estructurados individuales (no documentos completos)?
¿Son esenciales transacciones ACID que abarcan múltiples operaciones/entidades?
- Nota: Algunas bases documentales (MongoDB 4.0+) soportan transacciones multi-documento.
¿Las consultas usan principalmente claves primarias/índices simples (no consultas ad-hoc)?
¿Se filtran datos usando ≥3 atributos simultáneamente en >50% de las consultas?
¿Prefiere consultar datos anidados directamente en lugar de desnormalizar?
¿Las escrituras implican actualizaciones parciales complejas (no reemplazos completos)?
¿Requiere agregaciones multidimensionales (OLAP) sobre >1TB de datos?
¿Las consultas acceden a ≥3 entidades relacionadas en >40% de los casos?
¿Es crítico tener un esquema fijo para validar datos en ingesta?
¿Usa consultas geoespaciales o de grafos con frecuencia?
- Nota: Ambos modelos pueden soportarlo, pero con implementaciones distintas.
¿Necesita índices compuestos sobre múltiples campos anidados?
3. Requerimientos No-Funcionales
¿El volumen total estimado en 3 años es <50TB?
- Nota: Bases relacionales distribuidas (CockroachDB) pueden manejar petabytes.
¿La alta disponibilidad requiere consistencia fuerte (no eventual)?
¿El ratio lecturas/escrituras es >70/30?
¿Puede tolerar latencias >15ms en operaciones críticas?
¿El equipo tiene ≥2 años de experiencia con SQL?
¿Es esencial compatibilidad con herramientas BI tradicionales (Power BI, Tableau)?
¿Requiere replicación transaccional cross-region?
¿Necesita escalado horizontal automático (sharding) sin downtime?
- Nota: Algunas RDBMS (Vitess) permiten sharding con límites.
¿La carga incluye >50K operaciones/segundo sostenidas?
¿Los backups deben ser incrementales con recuperación a momento específico?
¿Puede aceptar bloqueos por migraciones de esquema (>1 min de downtime)?
5 Preguntas Críticas Decisivas
¿Es no negociable la integridad referencial entre entidades?
- Sí → Relacional (a menos que use extensiones como PostgreSQL + FOREIGN KEY en JSONB).
¿Los datos son >60% documentos anidados con estructura irregular?
- Sí → Documental (pero considere híbridos como MySQL + MongoDB).
¿Requiere JOINs complejos (>3 tablas) en >25% de las consultas?
- Sí → Relacional (aunque algunas documentales tienen
$lookupsimilar a JOINs).
- Sí → Relacional (aunque algunas documentales tienen
¿Necesita escalar horizontalmente sin límites prácticos?
- Sí → Documental (pero evalúe NewSQL como YugabyteDB).
¿Requiere transacciones ACID multi-operación en >30% de los casos?
- Sí → Relacional (pero verifique si su documental soporta transacciones).
Regla decisiva
Si ≥3 respuestas clave apuntan a una categoría, priorícela. En empates (2-2), evalúe el contexto del proyecto.
Interpretación de Puntajes
| Puntos Totales | Recomendación | Tecnologías Ejemplo |
|---|---|---|
| 28-35 | Relacional Puro | PostgreSQL, MySQL, SQL Server |
| 20-27 | Relacional + Extensiones | PostgreSQL (JSONB), SQL Server (XML), Oracle (JSON) |
| 15-19 | Híbrido o Multi-Modelo | MongoDB (transacciones), Cosmos DB (modo SQL), CockroachDB |
| 8-14 | Documental Puro | MongoDB, Couchbase, Firebase Firestore |
Conclusión
Este cuestionario te ofrece un marco estructurado para evaluar qué tipo de base de datos es más adecuada para tu proyecto. Si la mayoría de tus respuestas favorecen la integridad referencial, los JOINs y la validación de esquema, una base relacional es la mejor opción. Si en cambio tu proyecto requiere flexibilidad en la estructura de datos, escalabilidad horizontal y almacenamiento de documentos, una base documental puede ser la respuesta. En casos híbridos, considera soluciones como PostgreSQL con JSONB o bases multimodelo como CosmosDB. ¡Elige sabiamente para optimizar el rendimiento y la escalabilidad de tu aplicación!