Tracking de Pedidos en Tiempo Real con Angular, ASP.NET Core, SignalR y Azure Cosmos DB
Temas del Master que cubre este proyecto
Este proyecto integra contenidos de varias areas del Master en Desarrollo Full Stack + Arquitecturas Cloud:
- Arquitectura: SPA desacoplada Angular + API REST, diseno de APIs RESTful escalables y versionadas, patron Repository en ASP.NET Core
- Seguridad: Autenticacion y autorizacion con JWT, gestion de roles y politicas en APIs REST, proteccion CORS
- Computacion en la nube: Base de datos en Azure (Cosmos DB como equivalente a MongoDB Atlas en la nube)
- Comunicacion y Tiempo Real: SignalR en ASP.NET Core para aplicaciones en tiempo real, arquitectura orientada a eventos
El problema: como sabe el usuario que su pedido se ha actualizado?
Cualquier aplicacion de comercio electronico o logistica se enfrenta al mismo reto: el usuario quiere saber en que estado esta su pedido sin tener que recargar la pagina cada pocos segundos. El enfoque tradicional, el polling, implica que el cliente pregunta al servidor repetidamente si hay novedades. Es ineficiente, consume recursos innecesarios y ofrece una experiencia de usuario mediocre.
La pregunta es: como hacemos que el servidor avise al cliente en el instante en que algo cambia? Ahi entra SignalR.

Que vamos a construir?
Un sistema completo de seguimiento de pedidos en tiempo real con las siguientes caracteristicas:
- Dashboard en tiempo real que se actualiza sin recargar la pagina
- Dos roles diferenciados: Admin (puede crear y gestionar pedidos) y Viewer (solo lectura)
- Autenticacion con JWT y proteccion de endpoints por rol
- Base de datos en la nube con Azure Cosmos DB
La arquitectura del sistema
Antes de escribir una sola linea de codigo, es importante entender como se comunican las piezas:

Frontend (Angular 17): Es la SPA que el usuario ve en el navegador. Se conecta al backend mediante HTTP para autenticarse y obtener datos, y mantiene una conexion persistente con SignalR para recibir eventos en tiempo real.
Backend (ASP.NET Core 8): Expone una API REST securizada con JWT. Contiene un Hub de SignalR que actua como canal de difusion: cuando un pedido cambia de estado, notifica a todos los clientes conectados instantaneamente.
Base de datos (Azure Cosmos DB): Base de datos NoSQL distribuida en Azure donde se persisten todos los pedidos y su historial de estados.
Tecnologias utilizadas
- Angular 17 (Standalone Components, Signals, HttpClient)
- ASP.NET Core 8 Web API
- SignalR (Microsoft.AspNetCore.SignalR)
- JWT Bearer Authentication
- Azure Cosmos DB (SDK v3)
- .NET User Secrets (seguridad de credenciales en local)
- Python 3 (script simulador de eventos para el demo)
Paso a paso: como construir el sistema
Paso 1 – Configurar Azure Cosmos DB
Accede al portal de Azure (portal.azure.com) con tu cuenta y crea los siguientes recursos:
- Crea un grupo de recursos: rg-order-tracking
- Crea una cuenta de Azure Cosmos DB for NoSQL en modo Serverless
- Crea la base de datos TrackingDB y el contenedor Orders con partition key /id

Una vez creado, ve a la seccion Claves y copia la Cadena de conexion principal. Nunca la pongas en el codigo. Usa .NET User Secrets:
dotnet user-secrets set "CosmosDb:ConnectionString" "AccountEndpoint=...;AccountKey=...;"
dotnet user-secrets set "Jwt:Key" "TuClaveSecretaDe32CaracteresMinimo"
Paso 2 – El backend: ASP.NET Core + SignalR + JWT
2.1 Configurar autenticacion JWT
En Program.cs, registramos el esquema JWT y configuramos los eventos de SignalR para que el token pueda llegar como query parameter (requisito del handshake de WebSockets):
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.Events = new JwtBearerEvents {
OnMessageReceived = context => {
var token = context.Request.Query["access_token"];
if (!string.IsNullOrEmpty(token))
context.Token = token;
return Task.CompletedTask;
}
};
});
2.2 El Hub de SignalR
El Hub es el nucleo del sistema en tiempo real. Cuando un cliente se conecta, lo agrupamos por rol:
[Authorize]
public class OrderHub : Hub {
public override async Task OnConnectedAsync() {
await Groups.AddToGroupAsync(Context.ConnectionId, "AllUsers");
await base.OnConnectedAsync();
}
}
2.3 Disparar el evento desde el controlador
Cuando el admin actualiza el estado de un pedido, se guarda en Cosmos DB y se emite el evento a todos los clientes conectados:
var updated = await _cosmos.UpdateOrderStatusAsync(id, request.NewStatus, request.Note);
await _hub.Clients.Group("AllUsers").SendAsync("OrderStatusUpdated", new {
orderId = updated.OrderId,
status = updated.Status.ToString(),
timestamp = updated.UpdatedAt
});
Paso 3 – El frontend: Angular 17
3.1 El interceptor JWT
Un interceptor funcional de Angular anade el token JWT a cada peticion HTTP automaticamente:
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const token = inject(AuthService).getToken();
if (token) {
req = req.clone({ setHeaders: { Authorization: 'Bearer ' + token } });
}
return next(req);
};
3.2 El servicio de SignalR
El servicio gestiona la conexion persistente con el Hub y emite los eventos via RxJS Subjects:
this.hub = new HubConnectionBuilder()
.withUrl(environment.signalRUrl, {
accessTokenFactory: () => this.auth.getToken() ?? ''
})
.withAutomaticReconnect()
.build();
this.hub.on('OrderStatusUpdated', (event) => {
this.orderStatusUpdated$.next(event);
});
3.3 El dashboard en tiempo real
El componente escucha los eventos y actualiza el listado reactivamente con Angular Signals:
this.signalR.orderStatusUpdated$.subscribe(event => {
this.orders.update(orders =>
orders.map(o => o.orderId === event.orderId
? { ...o, status: event.status }
: o)
);
});


Paso 4 – Roles: Admin vs Viewer
La diferencia entre roles es funcional, no solo visual:
- Admin (fondo azul): puede crear pedidos, cambiar estados y ver el historial completo
- Viewer (fondo purpura): ve los mismos datos en tiempo real pero sin ningun control de modificacion
[HttpPost]
[Authorize(Policy = "AdminOnly")]
public async Task<IActionResult> Create([FromBody] Order order) { ... }
Paso 5 – Probar el sistema
Necesitas tres terminales abiertas:
# Terminal 1 - Backend
cd backend/TrackingApi
$env:ASPNETCORE_ENVIRONMENT='Development'
dotnet run
# Terminal 2 - Frontend
cd frontend
ng serve
# Terminal 3 - Simulador (opcional)
cd scripts
python simulate_orders.py --url http://localhost:5000 --orders 5 --delay 1.5
Abre http://localhost:4200 en dos ventanas del navegador. En una entra como admin/admin123 y en la otra como viewer/viewer123.

Problemas encontrados y como resolverlos
1. JWT en la negociacion de SignalR: SignalR no puede enviar cabeceras HTTP durante el handshake de WebSockets. La solucion es pasar el token como query parameter y leerlo en el evento OnMessageReceived del middleware JWT.
2. Enum serializado como numero: Por defecto, System.Text.Json serializa los enums como enteros. El frontend esperaba strings como «Pending» pero recibia 0. Solucion: anadir JsonStringEnumConverter en Program.cs.
3. User Secrets no se cargan en modo Production: .NET solo carga User Secrets en entorno Development. Hay que establecer $env:ASPNETCORE_ENVIRONMENT=’Development’ antes de lanzar el backend.
4. CORS y SignalR: SignalR requiere AllowCredentials() en la politica CORS, incompatible con AllowAnyOrigin(). Hay que especificar el origen exacto del frontend.
Recomendaciones
- Nunca pongas credenciales en el codigo. Usa User Secrets en local y variables de entorno en produccion.
- Activa withAutomaticReconnect() en el cliente SignalR para recuperar la conexion automaticamente.
- En produccion, considera Azure SignalR Service para escalar horizontalmente sin configuracion adicional.
- El modo Serverless de Cosmos DB es ideal para demos. Para produccion con carga constante evalua Provisioned Throughput.
- Serializa siempre los enums como strings en las APIs – hace el contrato mas legible y menos fragil.
Preguntas para el debate
WebSockets o Server-Sent Events? SignalR usa WebSockets con fallback a long polling. Para comunicacion unidireccional servidor a cliente, los SSE son una alternativa mas ligera. Cuando elegirias cada uno?
Hub propio o Azure SignalR Service? El Hub de ASP.NET Core funciona en un unico servidor. Si necesitas escalar horizontalmente, multiples instancias no comparten el estado de las conexiones. Donde esta el punto de equilibrio?
Roles estaticos o RBAC dinamico? En este proyecto los roles estan en codigo. En produccion deberian venir de un proveedor de identidad como Azure Active Directory. Que implica ese cambio?
Actualizacion manual o automatica? Los estados se cambian manualmente desde el panel de admin. En produccion vendrian de sistemas externos como GPS del transportista. Como diseñarias esa integracion?
Recursos
- Repositorio en GitHub – codigo completo del proyecto
- Documentacion oficial de SignalR para ASP.NET Core
- Azure Cosmos DB – introduccion
- JWT en ASP.NET Core
- Angular Signals – documentacion oficial
Autor/a: Sofia Rojas
Master: Desarrollo Full Stack + Arquitecturas Cloud
Centro: Tajamar Tech
Ano academico: 2025-2026
Codigo / recursos: Enlace a GitHub