Alexei Rojas Quiroga
← Todos los proyectos

Proyecto corporativo · Presentado sin referencias a la empresa: solo arquitectura y decisiones.

Plataforma de Apuestas Deportivas

Pipeline orientado a eventos: recepción, validación y registro de apuestas

Estado
En producción
Rol
Ingeniero backend senior: diseño de servicios y pipeline, handlers de Kafka, motor de límites en vivo, validación y entrega de apuestas en vivo, resiliencia, CI/CD y empaquetado para Kubernetes.

Arquitectura

  • Recepción y validación desacopladas con Kafka
  • Motor de límites compartido
  • Bloqueos por jugador, entrega idempotente a la API en vivo

La apuesta se acepta y se encola. Se revisan los límites y el historial del jugador y luego la apuesta aceptada se entrega a la API de apuestas del motor en vivo, que la inserta en la BD en vivo.

Arquitectura · 7 nodos · 2 flujos
LO QUE CONSTRUÍWeb clientFront-end deapuestasColoca apuestas, recibeestado en vivoASP.NET CoreAPI de recepción deapuestasAcepta apuestas y pre-verifica límites.NET libraryMotor de límitesReglas de apuesta purasy deterministasKafka + AvroFlujo de validaciónSolicitudes de apuestas.NET consumerServicio devalidación deapuestasAntigüedad, cuotas,límites, historialSQL ServerBase de datos deapuestas en vivoApuestas en vivo ehistorial para topesASP.NET CoreAPI de apuestas delmotor en vivoPertenece al motor deapuestas en vivo;inserta la apuesta1234567
  • Servicio / cómputo
  • Almacén de datos
  • Cola / mensajería
  • Cliente
  • Síncrono
  • Asíncrono / bucle
Desplázate hacia los lados para ver todo el diagrama

Cómo fluye, paso a paso

Haz clic en un paso para saltar a él. Haz clic en un componente para ver detalles.

Qué hace

Suite de microservicios backend para un operador de apuestas deportivas en línea. Recibe apuestas pregame y en vivo, las valida contra límites del jugador y cuotas vigentes, las persiste en el libro de apuestas y devuelve cada cambio de estado al front-end en tiempo real. Las apuestas en vivo añaden límites con historial revisados bajo bloqueo por jugador y luego una entrega idempotente a la API de apuestas del motor en vivo.

El problema

Una ruta monolítica y síncrona de inserción de apuestas no soportaba los picos de tráfico de eventos, no daba retroalimentación al jugador durante la validación y ataba las reglas de límites a un modelo heredado de procedimientos almacenados, difícil de probar y evolucionar. El operador necesitaba desacoplar recepción, validación y persistencia, mantener orden estricto por jugador e introducir reglas de límites en vivo más ricas sin romper el libro de apuestas heredado.

Qué construí

  • Diseñé un pipeline desacoplado con Kafka (recepción, validación, inserción) con un flujo de notificación dedicado, de modo que cada apuesta avanza por estados explícitos y el jugador siempre ve el progreso.
  • Extraje un motor de límites determinista y sin infraestructura como paquete NuGet versionado (límites estáticos más topes por juego con historial y reglas de superación), compartido por la API, el servicio de validación y un nuevo cliente Blazor.
  • Hice la validación de apuestas en vivo segura ante concurrencia con bloqueos de aplicación por jugador y semáforos acotados para consultas de catálogo, rechazando limpiamente cuando faltan datos de límites o son inconsistentes.
  • Añadí idempotencia y umbrales de antigüedad (las apuestas obsoletas se rechazan en validación y persistencia), con identificadores de transacción estables propagados en logs estructurados y notificaciones.
  • Encapsulé la biblioteca heredada de inserción de apuestas detrás de una API HTTP de compatibilidad, de modo que el nuevo pipeline pudo liberarse de forma incremental mientras el libro heredado seguía siendo la fuente de verdad.
  • Entregué CI/CD multi-entorno: etapas de pruebas unitarias y end-to-end, construcción de contenedores, despliegues con Helm y despliegue paralelo en IIS para componentes alojados en infraestructura heredada.

Decisiones clave y por qué

01

Kafka como columna vertebral entre recepción, validación y persistencia

El desacople permite que la API responda al instante, absorbe picos en días de eventos y deja que cada etapa escale y falle de forma independiente. Los mensajes se particionan por jugador para preservar el orden por cuenta, con commits manuales de offsets y reintentos acotados.

02

Motor de límites como biblioteca pura, no como servicio

Mantener las reglas libres de dependencias HTTP, de base de datos y de Kafka las hace totalmente probables y reutilizables en servidor y navegador. Quien llama aporta una instantánea del historial; el motor solo decide. Las versiones se fijan explícitamente para revalidar juntos los cambios de contrato.

03

Segunda validación autoritativa bajo bloqueo por jugador

Una apuesta en vivo se revalida contra la instantánea de límites más reciente y otra vez bajo un bloqueo por jugador, de modo que apuestas concurrentes no superen en conjunto los topes por juego. El endpoint público de pre-verificación es explícitamente preliminar.

04

Flujo de notificación dedicado que alimenta SignalR

Cada servicio emite los cambios de estado de la apuesta a un solo tópico; un único hub los distribuye a los clientes. Los servicios nunca hablan directo con el front-end, lo que los mantiene sin estado y escalables horizontalmente.

05

Envoltorio estilo strangler sobre la ruta heredada de inserción

Reimplementar años de reglas del libro era más riesgoso que encapsularlas. La biblioteca heredada se aloja tras una API HTTP, dando al nuevo pipeline un contrato estable mientras las reglas se migran funcionalidad por funcionalidad.

06

Clientes HTTP resilientes con timeouts explícitos

Todas las llamadas externas (cuotas, perfil de jugador, API heredada) usan clientes tipados con políticas de reintento, timeout y circuit breaker. Los fallos se traducen en claves de error de negocio explícitas en lugar de pérdidas silenciosas.

Stack tecnológico

Lenguajes
C#
Backend
.NET 10ASP.NET CoreSignalRPolly / Http.Resilience
Mensajería
KafkaAvro / Schema Registry
Datos
SQL ServerDapper
DevOps
OpenTelemetrySerilogDockerHelm / KubernetesGitLab CINuGet (private feed)
Pruebas
xUnit