5. Arquitectura y Diseño
5.6. Atributos de calidad
5.6.1. Modificabilidad
“Si el cambio es la única constante en el universo, entonces los cambios en el software no son solo constantes, sino que además son ubicuos” [23] .
Aumentar la cohesión
El equipo se aseguró de que los diferentes módulos del sistema sean altamente cohesivos y que tengan una única responsabilidad asociada para cumplir con una parte del RNF-MOD1 - Módulos . Como se puede ver en 5.3. Vista de Módulos , al seguir el patrón MVC y el patrón de capas, el equipo agrupa de manera lógica diferentes clases dentro de paquetes altamente cohesivos, lo que facilita su mantenimiento. Otra de las características es la separación de clases dentro de los paquetes para mantener módulos lo más pequeños posible. En la imagen debajo se puede ver por ejemplo que dentro de la capa de controllers , también hay una separación interna de clases específicas con responsabilidades bien definidas.
Ilustración 60 - Controllers del Backend .
Reducir el acoplamiento
Para reducir el acoplamiento se siguieron varias tácticas y patrones a diferentes niveles con el objetivo de cumplir con el RNF-MOD1 - Módulos .
Encapsulamiento y abstracción de servicios comunes: Es una de las formas más básicas de asegurar que a la hora de hacer cambios en un módulo, no se impacte otro en cuanto a
dependencias de su implementación. Esta táctica se puede ver reflejada en varios aspectos del sistema:
● El backend en su totalidad está encapsulado bajo una API REST, asegurando un contrato para quien quiera usarlo (el frontend en este caso). Se hablará de este punto por separado dentro de este atributo de calidad.
● Al seguir el “ Layered Pattern ”, cada una de sus capas encapsula, a través de varias interfaces, la lógica que mantiene detrás, pudiendo ser accesible únicamente por clases mayores o de la misma jerarquía. Básicamente, para acceder a cualquier
funcionalidad, se hace a través de su interfaz y no de su implementación. Un ejemplo posible es la interfaz responsable de proveer los pasos y puntos cardio:
Ilustración 61 - Ejemplo de Interfaz “ MeasuresDataServiceInterface ”.
Como se puede ver en el ejemplo, cuando el controlador encargado de brindar esta
información la necesite de su servicio, lo hace a través de una inyección de dependencia con la interfaz.
Ilustración 62 - Inyección de dependencia en el “ MeasuresDataController ”.
De esta manera, el controlador usa la funcionalidad de la interfaz sin importarle la implementación concreta, sabe que el resultado son los pasos o los puntos cardio. Esto permite que en caso de querer cambiar la fuente de información de GoogleFit
(implementación actual) a otro servicio, el impacto es solo a la hora de qué clase instanciar en tiempo de ejecución. Se crearía una nueva clase que implemente la misma
MeasuresDataServiceInterface pero que cambie la fuente de datos.
Ilustración 63 - Implementación concreta del “ MeasuresDataServiceInterface ”.
Uso de intermediarios: Los intermediarios son usados para romper dependencias entre componentes e incluso sistemas. La base de datos, usada algunas de sus tablas como cola de mensajes, funciona como un intermediario para manejar los diferentes eventos asincrónicos (se explicará este proceso en el punto de 5.6.4. Performance ). Sin embargo, el ejemplo más claro del uso de esta táctica y, como se pudo ver previamente en 5.3.3. Vista de Uso , es el paquete de providers ; quien contiene la lógica que hace posible el descubrimiento de servicios en tiempo de ejecución. Esto permite no solo resolver abstracciones de clases con sus
implementaciones al principio del ciclo de vida, sino que también permite registrar la relación entre eventos y escuchas.
El equipo aprovechó a su favor que Laravel permite implementar fácilmente el patrón de diseño Observer , permitiendo a diferentes escuchas, suscribirse a posibles eventos asociados a
modelos. Esta implementación permite que los objetos se comuniquen sin conocerse
directamente, reduciendo el acoplamiento y aumentando tanto la flexibilidad como el reúso.
Uso de estándares
La estandarización es una táctica asociada a la integrabilidad, interoperabilidad y mantenibilidad del código. Permite que una persona nueva (o la misma persona tiempo después) pueda entender el código y el diseño que se creó en el pasado. Por esa misma razón se siguieron varios estándares explicados en profundidad en 7.4.1. Estándares , permitiendo cumplir con el RNF-MOD2 - Estándares .
Configuración y variables de entorno
En este punto se cubre el cumplimiento del RNF-MOD3 - Configuración , gracias a las variables de entorno [24] y configuración de backing services como recursos conectables [98] .
La configuración incluye todo lo que puede variar entre despliegues en sus diferentes
entornos, desde credenciales hasta el uso de backing services . Aquí el equipo, inspirado en la guía 12 factor , decidió mantener una estricta separación entre la configuración y el código.
Ilustración 64 - Clases de configuración.
Como se puede ver, el equipo mantuvo archivos de configuración a través de los cuales se puede hacer referencia a las variables de entorno. El equipo se guió por la prueba de fuego de 12 Factor: Para saber si una aplicación tiene toda su configuración correctamente separada del código hay que comprobar que el código base puede convertirse en código abierto en
cualquier momento, sin comprometer las credenciales [24] , asegurando modificabilidad, mantenibilidad, integralidad y seguridad. Las variables de entorno se guardan en un archivo .env , el cual no se guarda en el control de versiones, y se puede modificar fácilmente entre entornos. En la siguiente imagen se puede ver como el archivo de configuración
database.php , depende enteramente del entorno en el que se ejecute.
Ilustración 64 - Archivo de configuración database.php.
El equipo mantiene diferentes valores de variables de entorno para cada ambiente. De esta manera se logra desacoplar la aplicación de su despliegue e incluso de sus backing services . Estos son configurados a partir de las variables de entorno, manteniendo las conexiones y secretos (no se hacen distinciones entre servicios locales y de terceros). Una forma práctica de ver este hecho, es por ejemplo el uso del servicio RDS contra una base de datos local, sin necesidad de cambios en el código (simplemente ajustando sus variables). Esto demuestra el bajo acoplamiento de la web app con su despliegue.
API REST
Cumpliendo con el RNF-MOD4 - Interfaces , el backend expone una API REST [99] . Esto permite no solamente un desacoplamiento total entre backend y frontend , sino que provee una forma estandarizada de trabajar entre cliente y servidor mediante solicitudes y respuestas
HTTP. Cada consulta no mantiene estado, esto reduce la complejidad, pero incrementa la dependencia a la base de datos para lograr manejarlo.
El equipo siguió la arquitectura como se plantea de forma habitual, usando los verbos GET, POST, PUT, PATCH y DELETE. Las rutas representan recursos del sistema con los que se puede interactuar. Cada endpoint expone una funcionalidad diferente, el frontend no se preocupa por la implementación de la misma, solo cumple con el contrato estipulado. En etapas tempranas del proyecto, esto fue muy favorable para poder desarrollar ambos sistemas en paralelo, siendo el contrato la única definición necesaria para poder hacerlo.
Para facilitar la visualización y pruebas de los mismos, se utilizó la herramienta Postman [100] . Esta herramienta además de permitir probar los endpoints , expone una documentación completa basada en cURL. Cualquier interesado puede formar parte del workspace del equipo y tener acceso a los endpoints como se ve en la imagen.
Ilustración 65 - Postman.
Se facilitó el uso del mismo entre ambientes gracias a la posibilidad de manejar variables de entorno con las diferentes URLs, y también el inicio de sesión seteando el token en una variable de colección haciendo que todas las demás hereden de ella.
Postman también permite la publicación de la documentación a un sitio web público en caso de que se requiera. En la imagen debajo se puede ver esta interfaz gráfica desde el navegador Google Chrome.
Todos los endpoints del sistema se pueden apreciar en Anexo 11.7. Listado de endpoints .
Ilustración 66 - Documentación web generada por Postman.