impr3siones – #PR1 Estructura del proyecto
La primera PR de #impr3siones contiene la estructura inicial del proyecto. Puedes encontrarla aqui #PR1 y te recomiendo echarle un ojo antes de seguir leyendo la entrada.
Ten en cuenta que el objetivo de esta primera PR es definir la arquitectura y comprobar que todo encaja y funciona correctamente, por eso verás algunos tests o modelos que la única función que tienen es ver que se importan bien desde el frontal.
Voy a profundizar muy poco en cada tema que toque en esta entrada, pero si tienes dudas recuerda que puedes usar los comentarios de esta entrada, y si te interesa algún tema en particular coméntalo y escribiré una entrada sobre ese tema.
Tecnologías escogidas
Lo primero a comentar son las tecnologías escogidas y el porqué las he escogido. Tenemos la suerte de que en el mundo del desarrollo de software existen alternativas para todo, lo que a veces puede ser confuso y puede hacernos pensar que no estamos escogiendo la mejor opción. En estos casos yo soy de la idea de que si una tecnología (lenguaje de programación, gestor de paquetes, framework etc…) es conocida automáticamente gana el doble de puntos, así que las alternativas tienen que ser muy superiores para empezar a considerarlas.
Eso si, si podemos escoger una tecnología que queramos aprender (como en este caso con NextJs), esa tecnología puntúa cuádruple 😅
📦 Gestor de paquetes – YARN
Si alguna vez has trabajado con un proyecto de Javascript/Typescript habrás usado un gestor de paquetes, y si tienes Nodejs instalado seguramente conozcas NPM. Yo me he decantado por Yarn básicamente por Yarn Workspaces.
Me gusta tener un monorepo y a la vez tener el código separado en distintos paquetes o módulos, de manera que pueda por ejemplo tener un paquete de modelos de objetos, que se importe desde el frontal web y desde las functions de firebase.
Los workspaces de Yarn se definen en el fichero package.json de la raíz. Yarn comprobara cuales son las dependencias comunes a los workspaces y las instalará en la carpeta node_modules común, y las dependencias particulares las instalará en el node_modules del espacio de trabajo. También creará enlaces simbólicos en el node_modules raíz hacia los workspaces para que estén disponibles como cualquier otro paquete.
La manera en la que luego se relacionan unos workspaces con otros es mediante el campo peerDependencies del package.json del workspace que quiere importar, un detalle importante a tener en cuenta es definir la misma versión en la dependencia que en el package.json del paquete importado.
☁️ Nube – Firebase
Existen multitud de soluciones cloud en el mercado, y yo me he decantado por Firebase por que es la que mas conozco, y porque aunque se necesite el plan de pago por uso para poder usar Firebase Functions, la capa gratuíta es mas que suficiente para la mayoría de mis proyectos.
Una cosa a tener en cuenta es que hay servicios de Firebase que corren sobre Google Cloud, como el storage o las functions, y aunque Firebase no soporta functions escritas en otros lenguajes, sí que se pueden tener si se despliegan directamente en Google Cloud.
Dentro de los servicios que ofrece Firebase hay dos bases de datos distintas, Realtime Database y FireStore. En este proyecto vamos a empezar a usar Realtime Database por ser mas sencilla, aunque la funcionalidad principal, que es la actualización de datos en tiempo real no la vayamos a usar.
🧑💻 Frontend – Next.js
Para construir el frontend vamos a utilizar NextJs. Se trata de un framework de React que llevaba tiempo queriendo aprender. Nos ofrece funcionalidades como renderizado en servidor, internacionalización, optimización de imágenes etc… Seguramente no usemos todas las funcionalidades, y otras como API Routes las sustituiremos por Firebase Functions
🇨🇳 Lenguaje de programación – TypeScript
El lenguaje de programación que entienden los navegadores es JavaScript (HTML y CSS digamos que van a parte) pero es un poco caótico por la falta de tipado, así que usaremos TypeScript, que si que tiene tipos y cuando se compila (transpila realmente) genera código JavaScript que nos servirá tanto para frontend y para backend, además podremos compartir los tipos entre todos nuestros módulos
🖋 Estilo de código – ESlint + Prettier
ESlint sirve para analizar nuestro código en base a ciertas reglas configurables, y nos permite detectar malas prácticas o fallos en el código antes incluso de compilar nada.
Prettier es un formateador de código, nos mantendrá todo el código bonito y legible, manteniendo espacios, comas, comillas etc… de manera que todo el código quede uniforme.
Puede parecer que ESLint y Prettier hacen lo mismo pero tienen funciones distintas, aunque puede que haya reglas entre ambos que colisionen, por lo que hay que tener un poco de cuidado a la hora de configurarlos
✅ Testing – Jest + React testing library
Los tests automáticos son una parte fundamental de todo proyecto, ya que nos descubrirán fallos antes de que nuestro código pase a producción. En este proyecto usaremos Jest y React Testing Library.
🤖 Automatización – Github Actions
Github Actions son acciones que se programan en el repositorio para que se ejecuten de manera automática con determinados eventos, nosotros de momento vamos a tener tres
- Etiquetador: añadirá etiquetas a las PR en función de los módulos que se modifiquen en la propia PR
- Linter: comprobará que se respetan todas las reglas de eslint, de manera que no podamos meter código en la rama main que rompa las reglas
- Tests: pasaremos test unitarios en las PR, ya hablaremos de esto en otra entrada, pero la idea no es pasar todos los tests en cada PR ya que habrá otro tipo de tests que tardarán bastante en ejecutarse. Aquí pasaremos solo los tests «rápidos».
Módulos
Cada espacio de trabajo de Yarn va a ser un módulo y me gusta categorizarlos en dos niveles.
Los de primer nivel de momento son el frontend y las functions. Se trata de módulos que tienen un objetivo o una misión propia, y que hay que desplegar en producción.
Los de segundo nivel serían los que no tienen misión propia y se usan desde los de primer nivel, en este caso estarían los modelos, utilidades comunes, fuentes de datos etc… Estos paquetes viven dentro de la carpeta /packages, y hay que tener en cuenta que no es sólo código común, puede que un paquete sirva solo a un workspace de primer nivel, pero exista sólo para tener el código algo mas ordenado.
Principales
Frontend
Este módulo es la interfaz de usuario. Hay que tener en cuenta que todo este código será enviado al navegador de los clientes, así que no debe de tener claves ni secretos de administración, ¡este código es público! lo que quiere decir que es susceptible de ser manipulado por lo que tenemos que tener mucho cuidado.
Functions
Usaremos Firebase Functions para ejecutar tareas que no queremos que se ejecuten en el lado del cliente. Este código se ejecuta en la nube de Google, por lo que usaremos estas funciones para tareas sensibles que no queremos que un «hacker» modifique, o que no podamos ejecutar desde el navegador del usuario, como enviar correos electrónicos.
Secundarios
Models
Aquí guardaremos todos los modelos comunes para compartirlos entre frontend y functions, de esta manera nos aseguramos de que las dos partes tengan una base común y no haya problemas entre ellos.
Data-connector
Este paquete tendrá la conexión a los servicios de Firebase, tanto Storage como Realtime Database, para el frontal. Aunque no sea código compartido prefiero tenerlo separado por limpieza y para evitar el acoplamiento, de esta manera si en algún momento queremos cambiar de nube no necesitaremos hacer cambios en el frontal y sólo en este paquete.
Firebase tiene dos sdks, el de cliente web y otro de administración. Cuando sea necesario el de administración crearemos otro paquete para tenerlos totalmente separados y evitar que las credenciales de administración se cuelen en el frontal.
Utils
El cajón de sastre donde guardaremos utilidades sencillas y comunes, como generación de ids, tratamiento de strings etc…
Creo que eso es todo, cualquier duda es bienvenida y nos vemos en próximas entradas ¡A partir de aquí empieza lo bueno 💪!