Evolución del proyecto

El objetivo de este proyecto fue desarrollar una aplicación de poker online que permitiera jugar con amigos en tiempo real, ya que las alternativas existentes no se ajustaban a lo que buscaba.

Además, siempre había tenido interés en el desarrollo de aplicaciones en tiempo real, pero no había tenido la oportunidad de profundizar en ello. Este proyecto surgió como una oportunidad para explorar este tipo de arquitecturas y comprender mejor los desafíos asociados.

Más allá de implementar el juego en sí, uno de los principales objetivos fue diseñar un sistema capaz de gestionar múltiples usuarios conectados simultáneamente, manteniendo la sincronización del estado de la partida en tiempo real.

Stack tecnológico


Framework Full Stack

  • NextJS

Base de datos & Tiempo real

  • Supabase + Supabase realtime

Arquitectura

  • Clean Architecture

Criterios de elección

1. Gran reactividad

Al no ser una pagina estática, si no, que un juego me pareció mucha mejor idea hacerlo con NextJS por facilidad en la reactividad, su middleware, pages route y api integrada. Todo esto sumado a su facilidad para desplegarlo en Vercel y la gran comunidad que tiene.

2. Simplicidad y rapidez de desarrollo

Priorice la velocidad y el poder empezar a ver demos reales antes que una gran boilerplate, a costa de una refactorización mas tardía para escalabilizar el proyecto.

3. Soporte para tiempo real

Dado que el proyecto requiere sincronización entre múltiples jugadores, era fundamental utilizar una tecnología que facilitara la comunicación bidireccional en tiempo real. Esto llevo a elegir supabase como base de datos por la gran comunidad, documentación y su fácil soporte a tiempo real.

4. Escalabilidad

Aunque el proyecto está orientado a partidas entre amigos, ha medida que escalo el proyecto se introdujeron arquitecturas que separaran la lógica de negocio/lógica del juego, haciéndola independiente de tanto de la base de datos como del frontend.

Problemas Principales


Código espagueti

Uno de los grandes problemas de priorizar la velocidad y las demos reales al empezar el proyecto fue terminar con un código sin ninguna arquitectura, con la base de datos y el frontend completamente acoplados, y toda la lógica del juego dentro de un único componente de React. Esto daba como resultado:

  • Código difícil de entender
  • Cambios y iteraciones complejas
  • Componente de React de casi 400 líneas

La solución a esto fue investigar sobre arquitecturas de software y elegí implementar Clean Architecture. Esta arquitectura me ayudaría a separar toda la lógica del juego y hacerla agnóstica tanto al framework como a la base de datos, pudiendo así reutilizar esta lógica en cualquier lugar, como una aplicación Android o Desktop. También me facilito muchísimo mas el testeo de la lógica del juego.

Poker architecture.png

Sincronización en tiempo real

Uno de los principales retos del proyecto fue gestionar correctamente la sincronización del estado entre múltiples usuarios.

En una primera implementación, no tenía del todo claro cómo funcionaban los canales de realtime de Supabase ni cómo se propagaban los cambios entre clientes. Esto provocaba problemas como:

  • Estados inconsistentes entre jugadores
  • Eventos que no se reflejaban correctamente en todos los clientes

A medida que fui profundizando en el funcionamiento del sistema de realtime (suscripciones, eventos y payloads), entendí mejor cómo debían estructurarse los canales y cómo gestionar los cambios de estado.

Tras ajustar la forma en la que se enviaban y recibían los eventos, conseguí:

  • Mantener una sincronización más fiable entre clientes
  • Reducir errores en la actualización del estado
  • Tener un flujo de comunicación predecible

Lentitud de la UI

En el sistema anterior, los cambios se enviaban directamente a la base de datos y, posteriormente, la interfaz se actualizaba mediante real time. Esto generaba una sensación de lentitud en la UI, ya que los botones no respondían de forma inmediata.

Para solucionarlo, implementé optimistic UI. Esto tuvo como coste mover parte de la lógica del dominio al hook del juego, pero permitió que la interfaz respondiera de forma instantánea a los eventos importantes. Más adelante, cuando llegaban los cambios del real time, la UI se sincronizaba con la base de datos, corrigiendo cualquier posible inconsistencia en caso de error.

Además, también había una sensación de falta de feedback al crear una partida, añadir jugadores o iniciar el juego. Aunque esta espera tenía sentido, era necesario indicar al usuario que la acción se había realizado correctamente. Para ello, añadí indicadores de carga que muestran en pantalla cuándo el sistema está procesando una acción.