Manual de arquitectura - Solify-IT/psyche-ing GitHub Wiki
Manual de arquitectura
Introducción
Este manual de arquitectura será la guía principal para el sistema "PSYCHE-ING" que será desarrollado durante el semestre Feb-Junio 2021. En el transcurso del semestre se irá actualizando mientras se encuentre más información necesaria o modificaciones en la visión de la arquitectura del sistema. El objetivo del manual es poder resolver dudas acerca del sistema y ser una fuente de información para todas las dudas relacionadas al sistema y su funcionalidad.
El manual tendrá enlaces a otras secciones de la wiki relacionadas a la arquitectura. Además, tendrá una explicación de porque cada decisión relacionada con la arquitectura del sistema fue tomada.
Atributos de calidad
Los atributos de calidad o requisitos no funcionales más importantes que deben ser cumplidos son:
- Security
- Implementación de acceso basado en roles para que cada usuario del sistema tenga distintos privilegios de acuerdo a sus funciones y proteger la información privada de los pacientes.
- Modifiability
- Facilidad de hacer cambios sin tener que dedicar mucho tiempo ni esfuerzo y a la vez teniendo una probabilidad muy baja de que estos cambios provocan un defecto.
- Usability
- Permitir al usuario navegar por el sistema de una forma fácil de comprender, intuitiva y sencilla de recordar.
- Availability
- El sistema deberá estar disponible en la gran mayoría del año. Se deberá implementar mecanismos que pueda restablecer el servicio en el caso de que el ambiente de producción llegara a fallar.
- En el caso de tener una conexión no estable debería poder utilizar datos guardados en cache para seguir operando mientras se restablece la conexión.
Backend
Para la arquitectura de backend o del lado del servidor de este sistema se utilizará el patrón de arquitectura de capas "Clean Architecture" por Robert C. Martin. Se recomienda leer sobre esto en el siguiente artículo:
Clean Coder Blog - The Clean Architecture
La estructura de directorios del proyecto seguirá la siguiente estructura:
Donde las capas representadas por diferentes arriba a abajo son:
- Capa 4: En esta capa están todos los "detalles" reemplazables. Como todo lo correspondiente a la base de datos y el framework que se utilizará.
- Capa 3: Convierte los datos entre la capa 4 y capa 2 + capa 3 de la forma mas conveniente. Ninguna capa abajo de esta debe conocer nada sobre la base de datos.
- Capa 2: Contiene reglas de negocio especificas de la aplicación. Encapsula e implementa los casos de uso del sistema. Esta capa no debe ser afectada por la base de datos, el UI, o el framework.
- Capa 1: Contiente reglas de negocio de la empresa. En esta capa, se encontraran entidades que pueden ser objetos o estructuras de datos con funciones. Son los menos probables de cambiar cuando algo externo cambia.
En la implementación del proyecto, se utiliza "decoradores" en los archivos de modelo de la capa 1 para poder así tener un ORM con TypeORM sin tener que repetir los modelos desde la capa de infraestructura (capa 4). Debido a que los decoradores son cosméticos, no afectan la estrategia de clean architecture. Si llegaramos a cambiar de estrategia para este "detalle" de implementación, solo se tendria que eliminar los decoradores en las entidades y las capas seguirán funcionando de forma normal.
Controllers y Presenters
En la capa 3, se convertirán los datos de la forma mas conveniente utilizando el siguiente pipe:
Para ver esto mas a detalle, se recomienda leer el siguiente artículo de controllers y presenters en Clean Architecture:
Implementing Clean Architecture - Of controllers and presenters
ORM
Para facilitar el manejo de la base de datos decidimos utilizar un ORM que se comunicara con la base de datos de PostgreSQL. TypeORM es una buena herramienta que nos puede servir debido a la arquitectura por capas y la facilidad de implementación de esta misma. TypeORM se encargará del manejo de la base de datos. Para ver documentacion acerca de como manejar las conexiones de TypeORM, puede utilizar la documentación oficial de TypeORM.
Los archivos de migraciones, manejo del ORM, y otros relacionados se pueden encontrar en la capa de infraestructura (capa 4).
Frontend
Single Page Application
La estrategia inicial era diseñar el sistema de una manera tradicional, utilizando una "multiple-page application". Esto es una aplicación que divide el sistema en "páginas" y realiza una petición completa en cada una.
Sin embargo, se ha hecho una tendencia utilizar "Single Page Applications". Estas son aplicaciones en las que solo se carga una sección utilizando maneras de comunicación asíncronas como AJAX. De esta forma, en vez de cambiar de "pagina" se cambia de "vista" y no es necesario hacer una petición completa de nuevo ya que solo se cargaría la sección que cambia.
En la siguiente imagen se puede ver la comparacion:
Arquitectura general de una aplicación SPA
Ventajas
- Rapido y responsivo
- Permite guardar datos en cache. Estos datos pueden utilizarse si el usuario no tiene conexión o tiene una conexión lenta. Posteriormente, los datos locales son sincronizados ya que se restablece la conexión
- Puede mejora la experiencia de usuario
- Facilita el desarrollo y debugging
Desventajas
- Debido a que solo se utilizará una url de página normalmente es difícil que motores de búsqueda como Google encuentran paginas asi. En el caso de PSYCHE-ING, esto no nos afecta ya que su uso será interno y no público
- Como solo se guarda una pagina en el historial del navegador, si el usuario le da retroceder al navegador no volvería al "estado" anterior de la aplicacion. Existen formas de solucionar este problema, pero si no se hace puede afectar en la usabilidad y en la experiencia de usuario
- Mas susceptible a Cross-Site Scripting (XSS) attacks
- Requiere de JavaScript activado
¿Cual se usará?
Haciendo esta comparativa y analizando los atributos de calidad necesarios para este sistema, se ha decidido utilizar Single Page Application ya que esta cumple con los atributos de Usability y Modifiability. El único atributo que puede ser afectado por esta decisión es Security. Para esto, es sumamente importante tener validaciones desde el lado del servidor.
Estructura de Archivos
El proyecto está estructurado por una agrupación por tipo de archivo, la cual básicamente consiste en conjuntar archivos similares. Así luce el árbol de directorios y carpetas:
En los archivos dentro de la carpeta “public” encontramos el archivo “index.html” con la estructura mínima de la aplicación y div identificado como “root” en el cual se insertarán todos nuestros módulos.
En la carpeta de imágenes se almacenarán aquellas que serán utilizadas a lo largo del proyecto.
Más tarde tenemos la carpeta “src” donde encontramos “index.tsx” el “main” de nuestra aplicación en React. En este se importa React, el React-dom (ya que trabajamos en entorno web) y los existentes de nuestra aplicación. También aquí es donde se inyecta el contenido en el div “root”.
Breve descripción del contenido de la carpeta “src”:
- api:
- Se contienen las funciones que hacen las peticiones al back.
- components:
- Se conjunta de carpetas específicas para cada tipo de componente del proyecto.
- interfaces:
- Se almacenan el conjunto de interfaces en las que se definen los atributos que necesitan.
- test:
- Se incluyen todas las pruebas necesarias a realizar en front.
- utils:
- Se integran funciones específicas que pueden ser utilizadas en cualquier parte del proyecto.
- views:
- Se contienen todas las vistas necesarias para cada ventana del proyecto.
Por último en la raíz de front, encontramos el archivo router en donde se declaran todas las rutas para en front. Así como también los archivos de css de raíz para utilizar a lo largo del proyecto.
Technology Stack
El stack tecnológico que se utiliza actualmente en detalle se puede encontrar en la siguiente página:
Autenticación
Para la arquitectura de este sistema, se utilizaran tokens de seguridad al momento de hacer peticiones de frontend a backend. Debido a la sencillez de la generación y la seguridad que esta proporciona, utilizaremos JSON Web Tokens (JWT) para la arquitectura. Al momento de autenticarse con la base de datos desde backend, el backend sera el encargado de generar este token para poder asi regresarlo al frontend al momento de hacer login y posteriormente el frontend deberá guardar este token en cache para poder reutilizarlo y mantener la sesión.
De esta forma, para cada petición de frontend a backend se pasaria el token y asi se aseguraría que el usuario esta autenticado exitosamente para hacer peticiones. Esta necesidad de generar un token aumenta la seguridad del sistema ya que evita que alguien acceda a un endpoint sin saber quien es en realidad.
Una vista general se puede ver en el siguiente diagrama:
Desde el lado de backend, se utilizara capas para procesar y verificar una petición de endpoint y para que el usuario este autenticado exitosamente.
Configuración de ambiente de desarrollo
La guia para la Configuración de ambiente de desarrollo se puede encontrar en la siguiente página:
Configuración de ambiente de desarrollo
Comandos útiles
Los comandos utilizados para este proyecto se pueden encontrar en la siguiente página: Comandos útiles
Pruebas Unitarias
Introducción
Al desarrollar un software debemos garantizar que el sistema cumple con los estándares de calidad necesarios y que el sistema funcione de manera correcta ante cualquier situación; es por esto, que es necesario la introducción de desarrollo de pruebas en este caso unitarias, para verificar que en cualquier situación que se pueda presentar el sistema sea capaz de reaccionar de manera apropiada.
Pruebas Unitarias
Las pruebas unitarias o también conocido como unit testing son una forma de comprobar que un fragmento de código funcione correctamente, es decir, el código hace lo que tiene que hacer.
Para poder probar nuestros códigos utilizamos el siguiente framework jest, el cual nos permite realizar estas pruebas unitarias.
Utilizando Jest
En nuestro proyecto, tanto en el back-end como el front-end tenemos la carpeta de test para el caso del back-end podemos probar:
- Interactor
- Presenter
- Repository
Esto, procede a que utilizamos a clean architecture y es lo mismo que se busca en los test, que a continuación se detallan.
Interactor
Para la parte del Interactor utilizamos principalmente las funciones Mock, prácticamente está parte consiste en actuar como un "espía" y mandar a llamar una función de manera indirecta.
Como podemos seguir en el siguiente ejemplo, pretendemos cachar el error del Repositorio de usuarios (userRepository) y cuál es el método que deseamos probar, para cumplir está parte de probar cierto fragmento de código. Una vez determinado estas partes, procedemos a hacer la "implementación" de ese método. Para esté caso, fingimos el throw new Error.
En la siguiente línea de const donde mandamos a ejecutar la función registerDoctorProfile, para finalmente utilizar expect, la cual revisamos si es que lo que esperábamos es igual a lo que realmente paso, como vemos esperamos un error y posteriormente esperamos que realmente sea un Nulo, ya que como se comento anteriormente este test se implementa como un espía.
Repository
En la carpeta de test/repository probamos principalmente la funcionalidad con la conexión a la base de datos, en la cual, esperamos que actúe de manera correcta y recibir ciertos resultados, por ejemplo, si queremos consultar algún registro que existe, esperaríamos que lo regrese de manera exitosa.
Como podemos ver el siguiente ejemplo, esperamos que nos generé un error puesto que el dato no existe, para esto, generamos la conexión con la base de datos, continuamos a llamar la función que vamos a evaluar, y como vemos, queremos encontrar el expediente con folio 99. Finalmente, validamos que recibamos los resultados deseados, que existe un error, es decir se definió y que el resultado sea nulo, pues como mencionamos, ese registro no existe realmente en nuestra base de datos.
Presenter
Al igual que los anteriores, este se ubica en test/presenter y es capaz de revisar que las llamadas a los métodos están respondiendo de manera adecuada y que por ende recibamos los datos solicitados ya pasados de la base de datos, por lo cual, esperamos tenga la estructura de la base de datos de un expediente.
En este caso, como podemos observar, deberíamos recibir el detalle del expediente cuando llamamos el método
Referencias
Clean Coder Blog - The Clean Architecture
A Comparison of Single-Page and Multi-Page Applications
Implementing Clean Architecture - Of controllers and presenters