En el mundo frontend es habitual no ver aplicaciones con un diseño de software similar a que se usa en backend.
Sin embargo, hay situaciones donde para desarrollar una aplicación frontend requiere de un diseño de software similar al de una aplicación backend.
Aquí algunos ejemplos de los que yo me he encontrado:
- Cuando el backend es de terceros.
- La aplicación debe funcionar en modo offline
- Cuando el backend es un server less tipo Firebase
En situaciones como estas es necesario separar bien las responsabilidades ya que la aplicación va a tener lógica de negocio.
Mal desde el comienzo
Puede parecer una tontería, pero desde el principio asumimos el concepto de una React app debido a que la herramienta que usamos para que nos genere el proyecto plantilla se llama Create React App.
Pero no existe tal cosa como la aplicación React.
De hecho en la página de React pone lo siguiente:
Una biblioteca de JavaScript para construir interfaces de usuario
Solo es para construir interfaces de usuario pero nos empeñamos en usarla para crear aplicaciones, es un error.
Quiero decir, lo correcto sería decir que es una aplicación frontend escrita en JavaScript o TypeScript que usa React para generar las vistas.
Y que además usa por ejemplo el SDK de Firebase.
¿Por qué le damos tanta importancia a la librería de UI?
No es justo llamarlas aplicaciones React, del mismo modo que no llamamos API Express a una API generada usando Express Js sino API Rest.
Si desde el comienzo damos tanta importancia a una de las herramientas, que no la única, usada para crear la app empezamos mal y con el enfoque incorrecto.
Responsabilidades
React ofrece una amplia libertad en términos del diseño de la aplicación y su estructura general, pero nos empeñamos en depender en exceso de ella.
Más veces de las que me gustaría me he encontrado algo similar a esto en los componentes React.
useEffect(() => {
fetch("api url")
.then((res) => res.json())
.then((remoteData) => {
const data = remoteData.map((item) => ({
prop1: item.prop1,
prop2: item.prop2,
prop3: item.prop3,
prop4: new Date()
}));
setData(data);
});
});
const handleSave = () => {
const data = {
prop1: ...,
prop2: ...,
prop3: ...
}
fetch('api url', {
method: "POST",
body: JSON.stringify(_datos),
headers: {"Content-type": "application/json; charset=UTF-8"}
}).then(response => ...)
}
// the actual rendering...
return ...
Las aplicaciones frontend no deben tratarse de forma muy diferente a otra aplicación de software.
Por un lado, estamos incumpliendo una serie principios SOLID o todos, pero lo más importante es que dependemos totalmente de React.
Si en el desarrollo de software es habitual usar la separación de responsabilidades para organizar la estructura del código, en frontend también es posible en cuanto hay más de una responsabilidad.
Aquí podemos ver que se mezclan varias responsabilidades.
Podemos ver de forma gráfica las responsabilidades de este componente.
Este tipo de componente suelen ser grandes y este es un code smell de que algo no va bien.
Cuando te encuentres un componente de muchas lineas de código generalmente es que hace demasiadas cosas.
Es necesario ir separando responsabilidades, el siguiente paso que suelo recomendar es mover todo lo que no sera renderizado a un custom hook.
Ahora la responsabilidad de renderizado es la única del componente y el custom hook tiene las demás.
De momento hemos dejado la responsabilidad render sin mezclarse con otras en el componente, es un paso.
Ahora el custom hook es el que tiene demasiadas responsabilidades y podemos repetir lo mismo, asignar un espacio a cada responsabilidad.
Utilizo los nombres que habitualmente uso con Clean Architecture pero realmente no es importante.
Si te fijas tenemos dos niveles de abstracción.
Por un lado tenemos todo lo que tiene que ver con presentación, dominio o datos.
Por otro lado tenemos un espacio para cada responsabilidad.
En este escenario final, ahora solo la responsabilidad render y gestión de estado esta acoplada a react.
Incluso la gestión de estado podría ser desligada de React utilizando el patrón Bloc si fuera necesario.
Esto va a depender de las características del equipo y los objetivos a perseguir.
En ocasiones no merece la pena, si la gestión de estado no va a ser reutilizada en otro contexto o si ya se estan usando custom hooks por el equipo y tienen interiorizado su uso.
Sin embargo, la capa de dominio y datos no dependen de React.
Si en un futuro React saca una versión con breaking changes o se decide sustituir por Vue, la parte afectada es menor y no supone un drama.
En algunos contextos podría tener sentido incluso paquetizar la capa de dominio o datos, para poder usarse desde otra aplicación. Por ejemplo como una versión móvil creada usando React Native.
He dejado fuera conceptos como inversión de dependencias y otros, que son importantes pero que queda fuera del objetivo del artículo.
En este artículo me he querido centrar exclusivamente en posibles pasos a dar a grandes rasgos, sin entrar en muchos detalle de cómo pasar de una React app a una app que usa React y que quede reflejado en el diseño.
Conclusiones
Como conclusión final, es importante cambiar desde el principio el punto de vista de como vemos nuestra app.
De React app a una app frontend que usa React. Y otras librerías, no nos olvidemos de esto último.
Al tener un enfoque diferente estamos en posición de hacer un mejor diseño de software.
En el curso de Clean Architecture hacemos ejercicios para aprender a hacer este tipo de diseño.