Cómo desarrollé mi juego de banderas

Recuerdo perfectamente jugar de pequeño a adivinar las capitales y los países por sus banderas, podía pasarme horas jugando y tratando de batir mi propio récord.

¿De dónde nace la idea del juego?

Desde que me dedico al desarrollo siempre he tenido pendiente hacer mi propio juego de banderas, no fue hasta que me crucé con una API de países cuando me plantee en serio llevar esta idea a algo material. ¡Fue un día trabajando, no recuerdo exactamente que estábamos haciendo, cuando encontramos esta API con países (bastante actualizada por cierto y que además se podía consumir de forma gratuita) cuando la guarde en favoritos y me dije a mi mismo, ahora o nunca!

El juego en sí es muy sencillo, tiene dos modalidades en las que se mostrará la siguiente información al usuario:

  • Capitales:
    • El nombre de un país con su bandera.
    • Una lista con 3 capitales.
  • Países:
    • Una bandera.
    • Una lista con 3 países.

En ambas modalidades el usuario deberá elegir la opción correcta, si esto ocurre se mostrará otra capital o país de forma recursiva hasta que falle.

Diseño y Arquitectura

Desde un punto de vista puramente técnico, el juego está dividido en varios servicios independientes que se comunican entre sí vía HTTP.

Podría haberlo hecho con un monolito todo junto, pero en este caso decidí separar el backend del frontend, tanto a nivel de lógica como de repositorio de código, lo que me permitió realizarlo en diferentes fases de una forma incremental.

Las principales piezas de esta arquitectura son:

Arquitectura

Persistencia de los datos

Este mismo artículo si lo hubiera escrito hace 3 años, os estaría contando como me había montado un Mongo tope gama, pero hoy os puedo decir con la cabeza bien alta que me estaría viniendo bastante arriba.

Es casi imposible no necesitar almacenar datos cuando estamos creando una aplicación, por lo que es muy habitual que nos flipamos bastante cuando llega la hora de elegir cómo hacerlo.

Este punto es para mí sin lugar a dudas en donde más cambio mental he notado a nivel profesional gracias a mis últimos años trabajando en un entorno ágil, tomando decisiones que no comprometan a tú yo del futuro y sobre todo eligiendo siempre la forma más sencilla de entregar valor para solucionar un problema.

Dada la naturaleza de los datos (el número de países es bastante estático y el juego solo los va a leer) es importante plantearse cuál sería la forma más sencilla y fácil de cambiar a futuro si fuera necesario de tener los datos accesibles para el backend.

Tal y como veo el desarrollo a día de hoy, esto es posible hacerlo con un simple fichero JSON (lo podéis ver aquí) que contenga todos los países y que el backend lo cargue en memoria cuando arranca. El fichero se puede sincronizar periódicamente para mantenerlo actualizado y con desplegar el backend los países estarán actualizados.


ACTUALIZACIÓN SOBRE LA PERSISTENCIA DE LOS DATOS

A raíz de comentar en Twitter que está escribiendo este post surgió una conversación super interesante con @javirevillas donde me comentaba que el había ido un paso más allá usando un gist como persistencia en una app móvil sobre el COVID que había creado hace tiempo.

Sinceramente nunca lo había pensado como una opción y me gusta mucho la idea.

  • Como punto super positivo veo que no sería necesario desplegar la aplicación para actualizar la lista de países, lo que para mí es una gran ventaja.
  • Como punto negativo esta solución tiene una dependencia externa que si fallara habría que manejar de alguna forma y eso añade complejidad a la solución.

IMPORTANTE No digo que no haya que usar bases de datos en nuestras apps (creo que es evidente que si), lo que quiero dar a entender es que muchas veces nos venimos arriba con las primeras decisiones en lugar de elegir la más sencilla en ese momento y que sea nuestra propia app la que nos diga si esta decisión se ha quedado obsoleta.

Backend

Actualmente estoy trabajando con Python y por tanto era la opción más lógica para implementar esta parte*. En mis proyectos personales son muy fan de FastAPI, ya que entre otras muchas cosas te proporciona “out of the box” la documentación de la API y el manejo de errores gracias a los tipos.

A nivel un poco más interno el servicio está diseñado usando Ports & Adapters o Arquitectura Hexagonal, tema sobre el que ya he hablado en otras ocasiones y que tiene las siguientes capas:

  • Delivery donde están definidos los endpoints de la API.
  • Casos de uso donde están definidos los casos de uso.
  • Infraestructura donde están las implementaciones que hablan con el exterior (en este caso la lectura del JSON).
  • Dominio donde están definidos nuestros modelos de dominio e interfaces.

Todo el código (puedes ver el repo aquí) está desarrollado aplicando TDD por lo que existen una serie de tests que se ejecutan en cada push.

La lógica del juego es muy sencilla:

  • Para iniciar una ronda se hace una petición GET al endpoint /api/v1/play:
    1. Se eligen de manera aleatoria 3 tres países.
    2. Entre estos 3 países se elige a su vez de forma aleatoria unos de ellos.
    3. Se envía la URL de la bandera, y su nombre si estamos en modalidad capitales, de este país junto con las capitales/nombres de país de los 3 países elegidos en el punto 1.
  • Para resolver una ronda se hace una petición POST al endpoint /api/v1/solve:
    1. Se envía como payload la opción seleccionada por el usuario junto con la URL de la bandera.
    2. Se valida que la opción es correcta y se notifica al frontend con el resultado.

Frontend

En lo que a la interfaz se refiere las dos modalidades del juego pese a ser casi idénticas están separadas tanto a nivel de código (capitals y countries) como de infraestructura y ambas que consumen la API del backend de forma muy similar.

Ambas están desarrolladas en Vue (siendo honesto esta parte es la que está más con pinzas, ya que el front no es mi fuerte), teniendo un componente principal responsable de interactuar con el usuario.

El juego tiene este aspecto:

Capitals & Countries

Cuando la web carga arranca un temporizador que se parará cuando el usuario falle una respuesta. En caso de acertar se pedirá una nueva ronda al backend de forma recursiva. En caso de que el usuario falle este podrá ver un resumen con el resultado de la partida, así como diversos enlaces para compartirlo.

Infraestructura

Hace tiempo que uso Netlify para alojar mi web personal y la verdad que funciona bastante bien para lo que son páginas estáticas (por ejemplo mi blog usando Hugo) o en este caso una SPA.

Como el frontend está desarrollado con Vue basta con crear una nueva página importando el código del repositorio para que Netlify lo detecte todo automáticamente y los despliegue.

El backend por otra parte está en Render, un servicio que nos permite desplegar nuestras aplicaciones de forma sencilla (podemos incluso montar un CI/CD que se despliegue automáticamente con cada push a nuestro repositorio) y gratuita (7500 horas mensuales).

Dado que el backend puede ser desde una app en Ruby on Rails hasta un servicio con Node, pasando por una API con FastAPI, la mejor forma de desplegarlo es haciendo uso de Docker. De esta forma Render no tiene que preocuparse los detalles internos y simplemente creará una imagen de nuestro servicio y lo desplegará.

El Dockerfile está disponible aquí por si le quereis echar un ojo.

Conclusiones

Espero que os haya gustado saber un poco más sobre como pienso a la hora de crear mis propias apps y que de paso hayáis podido descubrir algún servicio que no conocierais, nos vemos en la próxima!