Foto de perfil de Bastián Tapia
Bastián TapiaDesarrollador Full Stack
Saltar al contenido principal
Caso de Estudio Insignia · SaaS & ArquitecturaDic. 2025 — Presente

Plataforma SaaS Multi-Tenant para Gestión de Comunidades

Evolución de un desarrollo a medida hacia una plataforma SaaS con aislamiento lógico por tenant, control de cuotas y despacho asíncrono de eventos.

Arquitectura

Multi-Tenant

Núcleo compartido con aislamiento lógico por Tenant ID

Resiliencia

Rate Limiting

Protección de cuotas por tenant y buffering hacia APIs externas

Automatización

Async Queues

Procesamiento desacoplado de eventos y reintentos con backoff

Gestión

Panel Web

Configuración granular y control de permisos por comunidad

01Contexto y Necesidad

Resumen del Proyecto: Nació de la necesidad real de comunidades digitales que requerían herramientas de moderación, eventos programados, anuncios dinámicos y control de accesos sin tener que desplegar y mantener infraestructuras de software aisladas para cada una.

Problema Inicial: El enfoque inicial basado en soluciones a medida obligaba a clonar repositorios y bases de datos por cada comunidad cliente, encareciendo los costos de mantenimiento, fragmentando las actualizaciones y duplicando el esfuerzo operativo.

02Evolución Arquitectónica: De Monolitos Duplicados a Núcleo SaaS

Después: Núcleo SaaS Multi-Tenant Unificado

Arquitectura centralizada con motor de resolución de tenant, PostgreSQL compartido con aislamiento lógico y workers asíncronos.

[ Arquitectura SaaS Multi-Tenant ]
Clients / WebhooksTenant Resolution MiddlewareCore Multi-Tenant API
PostgreSQL Compartida

Scoping automático: WHERE tenant_id = $1

Worker Asíncrono

Token Bucket + Outbound Rate Limiter hacia APIs externas

Pilares de la Solución

  • Tenant Resolution Middleware dinámico por token JWT / payload
  • PostgreSQL con discriminador tenant_id e indexación compuesta
  • Colas asíncronas para webhooks y tareas con rate limiting integrado
  • Panel de control web unificado con configuración por tenant

Impacto Operacional & Negocio

  • Despliegue unificado de actualizaciones para todos los tenants
  • Aprovisionamiento instantáneo de nuevas comunidades sin nuevo hosting
  • Aislamiento lógico garantizado en capa de repositorios y queries
  • Consumo eficiente de recursos de base de datos y cómputo

03Ciclo de Vida de Peticiones y Resolución de Contexto

Traza paso a paso desde el ingreso HTTP / Webhook hasta la persistencia scoped y despacho de tareas.

Paso 01Node.js Crypto / Security Layer

Ingreso & Validación Criptográfica

HTTP Request o Webhook entrante

La petición llega al endpoint de la API. En el caso de webhooks de plataformas externas (Discord/Twitch), se valida la firma criptográfica (HMAC-SHA256) mediante comparación en tiempo constante para evitar ataques de temporización antes de procesar el body.

crypto.timingSafeEqual(calculatedSignature, headerSignature)
Paso 02TypeScript / Express-Node Context

Tenant Resolution Middleware

Extracción e Inyección de Contexto

El middleware extrae el identificador de la comunidad (tenant_id) a partir del subdominio, del token de sesión JWT o del payload del evento. Se valida la existencia y estado activo del tenant, inyectando un TenantContext inmutable en el ciclo de vida de la petición.

req.tenantContext = { tenantId: 'tenant_abc', plan: 'standard' }
Paso 03Token Bucket Algorithm / Memory Cache

Control de Rate Limiting & Cuotas

Evaluación preventiva de consumo

Se verifica la tasa de peticiones del tenant mediante un algoritmo Token Bucket en memoria. Si el tenant supera su umbral permitido, se responde inmediatamente con código HTTP 429 sin consumir recursos de base de datos ni saturar servicios aguas abajo.

if (!rateLimiter.consume(tenantId)) return res.status(429)
Paso 04PostgreSQL / Composite Indexing

Query Scoping en PostgreSQL

Aislamiento estricto de datos

La capa de repositorios exige el contexto del tenant en toda operación. Toda consulta SQL incluye obligatoriamente la cláusula WHERE tenant_id = $context.tenantId, aprovechando índices compuestos sobre (tenant_id, id) para evitar fugas cruzadas de datos.

SELECT * FROM community_configs WHERE tenant_id = $1 AND id = $2
Paso 05Async Worker Queue / Exponential Backoff

Despacho Asíncrono de Eventos

Worker Queue con Backoff Exponencial

Las acciones que requieren interacción con APIs externas (notificaciones de Discord, sincronización de roles, alertas en vivo) se encolan en segundo plano. El worker gestiona el despacho respetando los rate limits del proveedor externo y reintentando fallos transitorios.

taskQueue.enqueue({ event: 'ROLE_SYNC', tenantId, payload })

04Estrategia de Aislamiento de Datos en PostgreSQL

Decisiones de diseño para garantizar separación estricta entre comunidades y evitar accesos cruzados.

Modelo de Datos Elegido:

Base de datos relacional PostgreSQL compartida con discriminador de columna (tenant_id) e indexación compuesta.

¿Por qué no Schema-per-tenant? Se analizó la opción de crear un esquema PostgreSQL independiente por cada comunidad (Schema-per-tenant), pero se descartó en esta etapa porque aumentaba drásticamente la sobrecarga de conexiones en el pool de PostgreSQL y complicaba las migraciones DDL automáticas. La base compartida con tenant_id ofrece una relación óptima entre costo, mantenibilidad y rendimiento para este volumen de comunidades.

Mecanismos Anti-Fuga (Leak Prevention)

  • Enforcing a nivel de Repository Pattern: ningún método de consulta expone endpoints sin exigir el tenant_id autenticado.
  • Validación de pertenencia en mutaciones (UPDATE / DELETE) para asegurar que el registro pertenezca estrictamente al tenant emisor.
  • Foreign keys compuestas con tenant_id que impiden que una entidad hija referencie registros de otro tenant.
  • Validación de tipos estricta en TypeScript para el objeto de contexto de cada petición.

Estrategia de Indexación Compuesta

Creación de índices compuestos B-Tree sobre (tenant_id, id) y (tenant_id, created_at) en todas las tablas transaccionales, garantizando que el optimizador de PostgreSQL filtre inmediatamente por tenant sin escaneos completos de tabla.

CREATE INDEX idx_configs_tenant ON configs(tenant_id, id);

05Control de Rate Limiting & Resiliencia

Mecanismos de protección ante picos de tráfico y mitigación de cuotas ante APIs de terceros.

Algoritmo Token Bucket

Algoritmo Token Bucket con recarga continua de tokens por segundo según el plan de la comunidad.

Protección APIs Terceros

Amortiguador de peticiones hacia APIs externas (Discord/Twitch): las tareas se encolan y se despachan respetando ventanas de tiempo para evitar recibir bloqueos temporales de IP (HTTP 429 de terceros).

Control de Idempotencia

Control de idempotencia para webhooks entrantes mediante almacenamiento temporal de identificadores de evento procesados, evitando ejecuciones duplicadas ante reintentos de red.

Modos de Fallo Mitigados en Producción

Desconexión temporal del WebSocket de Discord → Reconexión automática con backoff exponencial y resincronización de estado.
Picos de peticiones en un tenant específico → Estrangulamiento selectivo (HTTP 429) sin degradar la disponibilidad para los demás tenants.
Fallo de entrega en webhooks externos → Reintentos automáticos con cola de fallos y alertas de error.

06Decisiones Técnicas & Trade-Offs Evaluados

Opciones arquitectónicas descartadas, justificación de la alternativa elegida y próximos pasos.

Estrategia de Almacenamiento Multi-Tenant
Alternativa Elegida:

Base de datos compartida con discriminador tenant_id e índices compuestos

Alternativa Descartada:

Esquemas PostgreSQL independientes por tenant (Schema-per-tenant)

Racional Técnico: Permite ejecutar migraciones de base de datos en un solo paso, reduce la presión sobre el pool de conexiones y minimiza costos de hosting durante las fases de crecimiento.

↳ Próxima Iteración: A medida que se incorporen clientes con requerimientos regulatorios estrictos, evaluar Row-Level Security (RLS) nativo de PostgreSQL o esquemas dedicados.

Arquitectura de Despliegue & Módulos
Alternativa Elegida:

Monolito modular con Worker de tareas asíncronas desacoplado

Alternativa Descartada:

Arquitectura de microservicios distribuidos

Racional Técnico: Mantiene la velocidad de desarrollo, tipado end-to-end garantizado con TypeScript y cero latencia de red entre servicios internos, evitando complejidad prematura de orquestación.

↳ Próxima Iteración: Separar el receptor de webhooks de alta frecuencia como una función serverless independiente si el volumen de eventos lo justifica.

Manejo de Tareas Asíncronas y Resiliencia
Alternativa Elegida:

Colas de tareas en memoria con control de tasa y backoff

Alternativa Descartada:

Llamadas HTTP síncronas directas a APIs de terceros

Racional Técnico: Garantiza que una caída o lentitud en APIs externas (Discord/Twitch) no bloquee la respuesta HTTP al usuario ni congele el servidor.

↳ Próxima Iteración: Incorporar Redis/BullMQ para persistencia duradera de colas ante reinicios de contenedores.

07Desglose de Infraestructura y Responsabilidades

Claridad técnica sobre qué capa asume cada componente del stack.

CapaTecnologíaRol en la ArquitecturaJustificación
Frontend & DashboardNext.js (React, TypeScript)Panel web de administración para moderadores y administradoresRenderizado optimizado, navegación rápida y protección de rutas con middleware de autenticación.
Core API BackendNode.js / Express (TypeScript)Lógica de negocio multi-tenant, resolución de contexto y endpoints RESTI/O asíncrono no bloqueante, contratos de tipos compartidos con frontend y ecosistema amplio de librerías.
Capa de PersistenciaPostgreSQLAlmacenamiento relacional de usuarios, configuraciones, eventos y auditoríaGarantías ACID, integridad referencial estricta, soporte de JSONB para configuraciones flexibles e indexación compuesta eficiente.
Workers AsíncronosNode.js Event WorkersProcesamiento de tareas programadas, anuncios y retries de webhooksAislar la ejecución diferida de tareas pesadas del hilo de respuesta de la API.
Contenedores & NubeDocker / Google Cloud Platform (GCP)Empaquetado reproducible y despliegue continuo de serviciosPortabilidad idéntica entre desarrollo local y producción con pipelines CI/CD automatizados.
Integraciones ExternasDiscord & Twitch APIsOAuth2, Webhooks, Gateway WebSocket Events y sincronización de rolesCanales nativos de interacción con los miembros y moderadores de las comunidades.

08Lecciones Clave y Criterio Técnico

  • Diseñar la separación de tenants en el modelo de datos desde el día uno evita migraciones de esquemas complejas en etapas posteriores.
  • Desacoplar las integraciones con APIs externas mediante adaptadores tipados y colas amortiguadoras previene bloqueos por rate limits de terceros.
  • El valor de priorizar la simplicidad operativa (DB compartida con tenant_id) antes de introducir complejidad prematura de microservicios o schemas independientes.
Disponible para squads de ingeniería & contratación

¿Quieres profundizar en este caso o discutir detalles de implementación?

Puedo explicarte en una entrevista las decisiones de diseño, el manejo de concurrencia y cómo construiría este sistema hoy.

Plataforma SaaS Multi-Tenant | Bastián Tapia