Arquitectura de Dashboards en Tiempo Real con SignalR y .NET 8: Paso a Paso

La mayoría de tutoriales de SignalR te enseñan a construir un chat. Está bien para aprender lo básico, pero no te dice nada sobre qué pasa cuando necesitás empujar 100K+ transacciones por día a un dashboard en vivo sin derretir tu servidor.
1. El Problema: Por Qué los Dashboards en Tiempo Real "Naive" Fallan
He construido dashboards en tiempo real en producción para sistemas financieros — monitoreo de transacciones, dashboards de compliance, paneles de reporting en vivo. Los patrones que uso son fundamentalmente diferentes de lo que encontrás en un tutorial típico de Medium.
El enfoque "obvio" para un dashboard en tiempo real se ve así:
- Llega una transacción → guardar en base de datos
- Inmediatamente broadcast a todos los clientes conectados vía SignalR
- Cada cliente re-renderiza el gráfico
- 10 broadcasts de SignalR/segundo × N clientes conectados
- 10 re-renders de Chart.js/segundo por cliente
- 10 queries a la base de datos/segundo si estás computando métricas en vivo
2. Vista General de la Arquitectura
El sistema corre como una sola aplicación Blazor Server con cuatro background services que forman un pipeline de procesamiento:
Cada componente tiene una responsabilidad única y se comunica a través de canales bien definidos. Voy a desglosar los tres patrones que hacen que esto funcione.
3. Patrón 1: Broadcasting por Lotes (Batched Broadcasting)
Esta es la decisión arquitectónica más importante. En lugar de hacer broadcast por transacción, acumulamos transacciones en un buffer y las enviamos como lote cada 500ms.
¿Por qué 500ms?
La percepción humana en dashboards hace plateau alrededor de 200–500ms. Por debajo de 200ms, los usuarios no distinguen actualizaciones individuales. Por encima de 1000ms, se siente lento. A 500ms obtenemos:
- Máximo 2 broadcasts/segundo independientemente del volumen de transacciones
- Una sola serialización por lote en vez de por transacción
- El cliente actualiza el gráfico una vez por lote, no por transacción
4. Patrón 2: Métricas Pre-Computadas
El dashboard muestra métricas como volumen total, tasa de éxito, TPS, y transacciones flaggeadas. Computar estas desde datos crudos en cada refresh es una sentencia de muerte a escala.
En su lugar, un background service pre-computa todo en un intervalo fijo y cachea los resultados: El broadcaster lee del cache — cero queries a la base de datos por refresh de cliente. Ya sea que tengas 5 clientes conectados o 500, la carga en la base de datos es la misma: una query de agregación cada 500ms.5. Patrón 3: Channel<T> como Bus de Mensajes Interno
El genera transacciones. El las persiste. El las envía a los clientes. Estos tres servicios necesitan comunicarse sin acoplamiento fuerte. provee exactamente esto — una cola productor-consumidor async-friendly, con límite de capacidad y backpressure incluido:¿Por qué Channel<T> y no ConcurrentQueue?
- Backpressure: bloquea al productor cuando el buffer está lleno — sin crecimiento ilimitado de memoria
- Enumeración async: en vez de loops de polling
- Cancelación: Soporte nativo de
- Múltiples consumidores: Tanto el como el pueden leer del mismo channel
6. El Hub de SignalR: Mantenelo Delgado
El hub en sí no hace casi nada — por diseño. Toda la lógica vive en los background services:
La interfaz tipada () asegura seguridad en compile-time — sin magic strings como .7. Estrategia de Base de Datos: Escrituras por Lotes
Llamadas individuales de a 100 TPS crean overhead innecesario. En su lugar, el acumula transacciones y las escribe en bulk: El flush se dispara cuando: se acumulan 100 transacciones O pasa 1 segundo — lo que ocurra primero. Esto da ~10x mejora en throughput sobre inserts individuales.8. Cliente: Blazor + Chart.js
El componente Blazor se conecta al hub y actualiza Chart.js a través de JavaScript interop:
Decisiones clave en el cliente
- Mantener solo 100 transacciones recientes en memoria — las más viejas se descartan
- Ventana deslizante de Chart.js de 5 minutos (máximo 300 data points)
- se llama una vez por lote, no por transacción
- Reconexión automática con backoff exponencial
9. Escalando: Qué Cambia a 500K, 1M+
La arquitectura en este repo maneja ~100K transacciones diarias en una sola instancia. Esto es lo que cambiarías al escalar:
| Escala | Cambio | Por qué |
|---|---|---|
| 500K/día | Particionamiento por fecha en MySQL | Performance de queries en tablas grandes |
| 500K/día | Redis para cache de métricas | Compartir cache entre múltiples instancias |
| 1M/día | Redis Backplane para SignalR | Escalado horizontal de SignalR |
| 1M/día | Réplica de lectura MySQL | Separar carga de lectura/escritura |
| 10M/día | Kafka reemplaza Channel<T> | Comunicación cross-service |
| 10M/día | Azure SignalR Service | WebSockets managed, auto-scaling |
| 10M/día | ClickHouse/TimescaleDB | Diseñado para agregación time-series |
10. Objetivos de Performance
| Métrica | Objetivo |
|---|---|
| Latencia de refresh del dashboard | < 100ms |
| Broadcast SignalR (hub → cliente) | < 50ms |
| Conexiones concurrentes | 500+ (instancia única) |
| Throughput de transacciones | 100+ TPS simulados |
| Memoria en estado estable | < 512MB |
11. Ejecutalo Vos Mismo
El dashboard empieza a generar transacciones simuladas inmediatamente. Abrí y mirá las métricas fluir. → Acceder a la demo en vivo12. Conclusiones Clave
- Nunca hagas broadcast por transacción. Agrupá en lotes de 500ms — los usuarios no notan la diferencia, tu servidor sí.
- Pre-computá todo. Las métricas del dashboard deben venir del cache, no de queries en vivo.
- Channel<T> está subestimado. Es el bus de mensajes interno perfecto para .NET — async, con límites, y backpressure.
- Mantené el hub delgado. La lógica de negocio va en los servicios, no en el hub de SignalR.
- Diseñá para la próxima escala. Los patrones de este repo funcionan a 100K/día; los mismos patrones con diferente infraestructura funcionan a 10M/día.
Esta arquitectura está basada en patrones que he implementado en producción para sistemas de monitoreo de transacciones financieras. El código fuente tiene licencia MIT — cloná, aprendé, adaptalo para tu caso de uso.
→ Código Fuente Completo en GitHub→ Contacto — Sin pitch de ventas, solo una conversación técnica honesta.
Artículos Relacionados
Multi-tenant SaaS en .NET: arquitectura segura para escalar sin reescribir
Guía práctica de arquitectura multi-tenant en .NET: patrones, seguridad, EF Core y migración desde single-tenant sin romper tu producto.
RAG: La Tecnología que Permite Preguntarle a tus Documentos — Qué Es, Cómo Funciona, Cuánto Cuesta y Cuándo NO Usarla
Guía completa sobre RAG (Retrieval-Augmented Generation): qué es, cómo funciona paso a paso, glosario completo, casos de uso reales, cuándo NO usarlo, y tabla de costos reales con números de OpenAI. Sin hype, sin ventas.
Cómo la IA cambió mi forma de programar (y no fue como esperaba)
Después de 7+ años construyendo sistemas para banca y seguros en Panamá, la IA transformó mi flujo de trabajo. Pero el secreto no está en los prompts — está en el contexto.
¿Listo para iniciar tu proyecto?
Hablemos de cómo puedo ayudarte a construir soluciones modernas y escalables para tu negocio.
Contactar