Saltar al contenido
Volver a proyectos
Propio
2026

WorldCup 2026

Plataforma de predicciones deportivas con acceso por invitación, ranking en vivo y cobro en línea.

27

Azure Functions

25+

componentes React

0

costo fuera de picos

CI/CD

despliegue automático

El problema

Una comunidad quería competir prediciendo los resultados del Mundial, pero necesitaba mucho más que un formulario: registro cerrado por invitación, puntuación automática apenas termina cada partido, ranking confiable, cobro de la inscripción y todo funcionando en el celular. Sostener un servidor encendido durante un mes para picos de tráfico que solo ocurren durante los partidos no tenía sentido económico.

La solución

Construí la plataforma completa: un backend serverless de 27 Azure Functions sobre Cosmos DB y una aplicación React instalable como PWA. El sistema gestiona invitaciones cifradas, autenticación, predicciones, un motor de puntuación, rifas, eventos, notificaciones por correo y el cobro con confirmación por webhook de la pasarela.

La decisión técnica que lo sostiene

Arquitectura serverless con escala a cero. El costo sigue al uso real en lugar de a un servidor encendido las 24 horas, y los picos durante los partidos se absorben solos. La sincronización de resultados no depende de que nadie entre a la aplicación: una función por temporizador consulta la fuente de datos deportivos, detecta los partidos terminados y dispara el recálculo de puntos de todas las predicciones. Los tokens de invitación se cifran con AES-256 y llevan su propia fecha de expiración dentro, así que no hay que consultar la base de datos para saber si una invitación sigue viva.

El resultado

Sistema en producción con despliegue automático: cada push a main compila y publica el frontend y las funciones sin intervención manual. La puntuación es automática y auditable, y la infraestructura cuesta prácticamente cero fuera de los días de partido.

Arquitectura

PWA React 19instalable en el celularStatic Web AppsCDN + rutas SPAJWTAzure Functions27 funciones · .NET 8Cosmos DBdatos del torneoTimer: SyncResultscierra partidos y puntúaPasarela de pagowebhook firmadowebhookGitHub Actionspush a main → producciónCI/CD

Las líneas punteadas son flujos que ocurren solos, sin que nadie esté usando la aplicación.

Cómo se ve por dentro

Ejemplos ilustrativos de los patrones aplicados, con la razón de cada decisión.

Clave de cifrado fuera del código

csharp
public EncryptionService(ILogger<EncryptionService> logger)
{
    _logger = logger;
    _encryptionKey = Environment.GetEnvironmentVariable("ENCRYPTION_KEY")
        ?? throw new InvalidOperationException(
            "ENCRYPTION_KEY environment variable not set");
}

La clave de AES-256 nunca vive en el repositorio: se inyecta como variable de entorno y el servicio falla al arrancar si no está configurada. Es preferible que el sistema no levante a que levante en silencio con una clave insegura.

Ejemplo ilustrativo del patrón aplicado; no es código de ningún producto.

Sincronización de resultados sin intervención humana

csharp
private const int BufferMinutes = 115;

[Function("SyncResults")]
public async Task<HttpResponseData> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "sync-results")]
    HttpRequestData req)
{
    var allMatches = await _matchRepository.GetAllAsync();
    var updatedCount = 0;

    foreach (var match in allMatches.Where(EstaListoParaCerrar))
    {
        var resultado = await _footballDataService.GetResultAsync(match.ExternalId);
        if (resultado is null) continue;

        await _matchRepository.SetScoreAsync(match.Id, resultado);
        await _scoringService.RecalculateAsync(match.Id);
        updatedCount++;
    }

    return await req.OkAsync(new { updated = updatedCount });
}

La función recorre los partidos, consulta la fuente deportiva y recalcula la puntuación de las predicciones afectadas. El margen de 115 minutos evita cerrar un partido que aún puede tener tiempo añadido o prórroga.

Ejemplo ilustrativo del patrón aplicado; no es código de ningún producto.

Stack del proyecto

¿Necesitas algo parecido?

Cuéntame tu caso y te digo con franqueza si es viable y cuánto toma.

Hablemos