Blog de Amazon Web Services (AWS)
Como construir y desplegar una aplicación web moderna en 3 semanas con AWS Amplify
Por Juan Nicolas Bolaños y Juan Miguel Bermudez Mieles, Arquitectos de Soluciones en Amazon Web Services para Sector Público.
Introducción
En Amazon Web Services nuestra cultura está definida por una serie de principios de liderazgo, uno de ellos se llama: Esforzarse por ser el mejor empleador de la tierra. Partiendo de lo anterior, se creó una iniciativa llamada La Movida, la cual buscaba que los y las Amazonians hicieran más actividades físicas para romper un poco la rutina, buscando tanto la salud mental como física de todos.
Surge entonces la necesidad de crear una aplicación web que soportara la iniciativa, desde el registro de participantes hasta la trazabilidad de las actividades, cómo su tipificación. Esta aplicación permitiría cargar las distintas actividades de distintos deportes, con o sin fotos, tiempos de duración, etc. ¿El reto? La Movida arrancaba en tan solo 3 semanas.
En este blog podrá encontrar todo acerca de nuestra experiencia en el proceso de construir la app de la forma más ágil y efectiva posible; Sin sacrificar rendimiento, escalabilidad, seguridad, simplicidad de la operación o resiliencia de la misma. También mencionaremos las lecciones aprendidas y oportunidades de mejora para futuras iteraciones
La caja de herramientas
Cuando hablamos de crear un aplicativo web full stack existen numerosas opciones. Cada una de ellas con sus ventajas y limitaciones. Algunas opciones ofrecen una mayor posibilidad de personalización, otras te dan un punto de arranque rápido y sencillo.
Inicialmente, se pensó en un desarrollo compuesto por una backend en FastAPI (Python), frontend en React y Bootstrap. Amazon Cognito, un servicio de identidad y acceso de usuarios para la autenticación. Amazon DocumentDB, un servicio de base de datos JSON administrado compatible con MongoDB como base de datos no relacional. Por último, AWS CDK, un kit de desarrollo de la nube de AWS que permite el manejo de la infraestructura donde iban ser desplegados estos componentes. La ventaja de estas tecnologías yace en usar de forma independiente distintos lenguajes de programación y personalizar cada uno de los componentes al nivel de detalle deseado.
Sin embargo, lo que nos hizo tomar otro camino, era la complejidad agregada al trabajar con componentes tan desacoplados, como por ejemplo: Administrar de forma independiente cada pila de infraestructura aprovisionada con AWS CDK (Balanceadores de carga, CDN, etc) o el ciclo de vida de los tokens de Amazon Cognito, para la autenticación y autorización de los usuarios. Por último, había que crear desde cero una aplicación frontend con diseño adaptable (Responsive design).
Entonces, era necesario pensar en un conjunto de soluciones y componentes que nos permitiera ser lo más ágiles posible. Queríamos una solución que cumpliera con lo siguiente:
- Delegar el trabajo indiferenciado y enfocarnos en desarrollar la aplicación.
- Evitar preocuparse del aprovisionamiento y despliegue de infraestructura.
- Hacer uso de servicios administrados (De preferencia sin servidor o “serverless”) para simplificar la operación.
- Contar con un marco de frontend que nos permitiera ofrecer una robusta y moderna aplicación web con una buena experiencia para el usuario. Sin importar desde donde se acceda, ya sea un celular, tablet o computadora.
Para conseguir los objetivos planteados con anterioridad, se optó por usar AWS Amplify. AWS Amplify permite desplegar aplicaciones web y móviles full stack. Tiene capacidades para implementar aplicaciones de frontend estáticas, desarrollo de interfaz de usuario (Desde el prototipo, hasta la generación automática de código fuente para los componentes). Además, Amplify tiene la capacidad de crear backends completos desde cero usando servicios y mejores prácticas de AWS para agregarlos a frontends existentes o nuevos. Dentro del backend, se puede agregar características como autenticación, almacenamiento, entre otras de forma rápida y sencilla. Su integración con flujos de GIT, distintos marcos de frontend y las capacidades de la nube de AWS iba a permitir que nuestra implementación fuese más ágil y efectiva. De acuerdo a lo mencionado, AWS Amplify fue uno de los ganadores en nuestra decisión final.
Solo faltaba definir como íbamos a construir el frontend. Nuevamente, el ahorro de tiempo era primordial para cumplir la meta de las 3 semanas. Por ende, buscamos una opción que nos permitiera utilizar componentes de interfaz pre-construidos, que tuviera un lenguaje de diseño consistente y fuera fácil de implementación las mejores prácticas para la experiencia de usuario.
Nuestro segundo ganador fue Cloudscape Design System, un sistema de diseño de código abierto para crear aplicaciones web. Creado en 2016, es utilizado en los productos y servicios de AWS hoy en día. Ofrece pautas de interfaz de usuario, componentes de front-end, recursos de diseño y herramientas de desarrollo para crear experiencias de usuario intuitivas, interactivas y atractivas a cualquier escala.
Figura 1: Ejemplo de interfaz de usuario con Cloudscape Design System
La imagen anterior corresponde a una de las interfaces “demo” con las que cuenta Cloudscape. Existen muchas más donde se pueden encontrar tablas, formularios, tableros y entre otros. Cada uno tiene su enlace a un repositorio Git donde se puede explorar el código fuente. Esto es sumamente útil para replicar algunos patrones de diseño y personalizarlos al “Look and Feel” deseado.
La arquitecura
Nos vamos a adentrar en el esqueleto de la aplicación, donde vamos a explicar cada uno de los componentes y su funcionalidad. A continuación, se encuentra el diagrama de arquitectura junto a los servicios que fueron usados para la aplicación:
Figura 2: Arquitectura de solución de la aplicación
AWS Amplify
AWS Amplify simplifica el despliegue de una gran variedad de características para nuestra aplicación dependiendo de la necesidad. Desde componentes muy transversales como la autenticación y bases de datos, hasta características mucho más específicas como predicciones AI/ML (Inteligencia Artificial/Machine Learning) o servicios de geolocalización.
Cada uno de ellos se conoce como un “Amplify Feature” o una capacidad provista de AWS Amplify. Aquí no solo se contempla la infraestructura desplegada y administrada, también se incluyen librerías para diversas plataformas/lenguajes de programación y mejores practicas. Cada capacidad provista esta orientada para resolver la implementación de las características anteriormente mencionadas.
Dentro de nuestra aplicación decidimos utilizar los siguientes módulos:
- Autenticación: Con Amazon Cognito, se desplegó tanto un Pool de Usuarios, donde se autentican y autorizan los usuarios, como también un Identity Pool. El segundo, permite que un usuario de Amazon Cognito pueda acceder a recursos de AWS. Esto permite que se pueda consumir de una forma segura el contenido multimedia que almacenamos en la aplicación y se pueda interactuar con otros servicios de AWS.
- API (GraphQL): Con AWS AppSync, podemos desplegar un API tipo GraphQL totalmente administrado por AWS. Este recurso se puede conectar a distintas bases de datos (relacionales y no relacionales) o integrarse con otras fuentes a través de AWS Lambda (Este servicio se encuentra en “Funciones”). La forma más rápida y sencilla es integrar con una base de datos noSQL en Amazon DynamoDB. A partir de un esquema de datos, AWS Amplify se encarga por completo entre la integración de estos 2 componentes. Así mismo, nos ofrece distintos métodos de autenticación como la llave de API que expone AppSync, el User Pool de Amazon Cognito o incluso crear nuestras propias reglas de autorización a través de AWS Lambda.
- Almacenamiento: Con Amazon Simple Storage Service (O simplemente, Amazon S3), tenemos acceso a un servicio de almacenamiento por objetos, donde podemos almacenar y proteger cualquier cantidad de datos. A la vez, nos da un mecanismo sencillo para administrar el contenido de nuestros usuarios en la app. Incluso, a través de AWS Amplify podemos hacer una segmentación del acceso del contenido, desde usuarios invitados que pueden ver el contenido disponible para ellos, como contenido completamente privado a cada uno de los usuarios registrados.
- Funciones: AWS Lambda nos permite ejecutar código sin aprovisionar ni administrar servidores. En. nuestra aplicación lo usamos para personalizar el ciclo de autenticación de Amazon Cognito. También, lo usamos para crear una función que se ejecuta una vez por semana para otorgar bonificaciones de puntos a los equipos que sobrepasaron cierto número de actividades físicas.
- Alojamiento: Amplify Hosting, proporciona un flujo de trabajo basado en Git para alojar aplicaciones web completas sin servidor con despliegue continuo (CI/CD). Amplify integra la red de distribución de contenido (O CDN, por sus siglas en inglés) de Amazon CloudFront con la característica de Amazon S3 para alojar sitios web estáticos. Aquí es donde se despliega nuestra aplicación web hecha en el marco ReactJS junto con el sistema de diseño de Cloudscape Design.
Adicional a los recursos desplegados y administrados por AWS Amplify, agregamos otros servicios independientes para complementar la solución:
Adicional a los recursos desplegados y administrados por AWS Amplify, agregamos otros servicios independientes para complementar la solución:
Una de las facilidades que nos brinda AWS Amplify, es la integración con otros servicios de Amazon Web Services, como lo son Amazon Route 53 y AWS Certificate Manager. Lo anterior, nos brinda la posibilidad de asignar nombres de dominio personalizados para nuestros ambientes. También, al usar las zonas alojadas de Amazon Route 53, podemos solicitar un certificado SSL/TLS para todos los dominios de nuestra aplicación de forma gratuita con AWS Certificate Manager o usar el certificado administrado predeterminado que Amplify proporciona.
Integración Continua/Despliegue Continuo (CI/CD)
AWS Amplify se integra con repositorios Git para el despliegue de nuevas versiones, tanto cuando se detectan nuevas confirmaciones (O “commit” en inglés), como cuando se autoriza un “Pull request” a la rama que se conecta con AWS Amplify. Esto permite despliegues de backend condicionales, donde se puede re-utilizar la misma estructura de backend para varias ramas del front-end.
Estas características facilitan crear ambientes como desarrollo, pruebas y producción. Donde cada ambiente puede tener su propia configuración de backend y frontend, cada uno se asocia a una rama respectiva de Git para despliegues automatizados. Además, permite configurar variables de entorno independientes para cada ambiente. Todo esto facilita la promoción de código entre ambientes (ej. de desarrollo a producción), acelerando la ejecución de pruebas y detección fallas a tiempo que nos permita hacer rollback de forma rápida.
Implementando nuestra aplicación
Crear la aplicación
Existen numerosas formas de interactuar con AWS Amplify, a continuación se listan las que fueron usadas en este proyecto. Sin embargo, existen otras rutas para llegar a resultados similares dependiendo de las necesidades.
Levantar una aplicación sobre AWS Amplify es un proceso rápido y sencillo. Solo es necesario cumplir con una serie de pre-requisitos como una cuenta de AWS, la CLI (Interfaz de comando de linea) de Amplify, y un usuario IAM con permisos suficientes para hacer el despliegue desde dicha CLI. Después, se elige uno de los marcos soportados para implementar el proyecto, en nuestro caso fue ReactJS (Es importante tener instalado el administrador de dependencias como NPM para el caso de JavaScript). Por último se crea la aplicación siguiendo los comandos que se listan en esta guía.
Una vez se encuentra creada la aplicación, el ambiente backend de amplify establecido y las dependencias necesarias instaladas, lo siguiente es agregar los componentes necesarios. El proceso es simple: En la CLI se debe poner el comando amplify add <x>
donde <x>
puede ser la autenticación, el API, almacenamiento, etc. La CLI va a desplegar las opciones de configuración correspondiente. Al finalizar, en el directorio amplify/backend
del proyecto estará la nueva carpeta que corresponde al componente agregado.
Desplegar cambios
Una de las ventajas de amplify es la capacidad de manejar distintos ambientes en simultaneo, se puede hacer una segmentación acorde al ciclo de vida del desarrollo de la aplicación. Se pueden levantar ambientes para desarrollo, pruebas y producción.
Esta característica resulta de utilidad para implementar un ambiente de pruebas/staging. Aquí se puede comprobar la calidad de una nueva versión antes de ser liberada a producción.
Para desplegar todos los cambios basta con ejecutar amplify push
en la CLI. Esto genera un despliegue a través de plantillas de infraestructura como código que Amplify crea y administra. Es totalmente transparente para el desarrollador!
El frontend funciona de forma diferente, es necesario ejecutar amplify add hosting
. Este comando agrega el componente de alojamiento, en donde puedes elegir entre la integración con tu repositorio git o hacer los despliegues de forma manual. En el caso de la segunda opción, una vez agregado dicho componente, con el comando amplify publish
se despliega una nueva versión del frontend.
Amplify Studio
Si eres alguien que prefiere una interfaz más gráfica, existe Amplify Studio. Es un entorno de desarrollo visual para construir las mismas aplicaciones full-stack para web y mobile. Studio aprovecha las capacidades existentes de AWS Amplify para brindar una forma rápida de desarrollar e implementar el proyecto a través de una interfaz de usuario intuitiva y simple.
Figura 3: Interfaz de Amplify Studio
En Studio puedes usar el diseñador de modelo de datos para crear tu entidades, atributos y relaciones entre ellos. Configurar autenticación con proveedores externos (Amazon, Google, Facebook, etc.), definir reglas de autorización para el acceso a los datos, crear almacenamiento de archivos, escribir funciones lambda y entre muchas otras características.
Una de las capacidades más sorprendentes que tiene Studio es su integración con Figma, una herramienta de prototipado y diseño de interfaces de usuario. Dicha integración, hace posible la generación automática de componentes de ReactJS a través de la librería Amplify UI. Esto permite diseñar de forma visual en Figma, importar los diseños a Studio y generar automáticamente código que puede ser reutilizado en el frontend. Además, estos componentes se pueden integrar con el modelo de datos para que desplieguen la información apropiada al momento de ejecución.
Studio también es una herramienta útil para administrar la configuración y contenido de la aplicación. Tiene interfaces para modificar el backend, donde se puede ver y modificar componentes individuales. Así mismo, se puede acceder a la información de la base de datos y realizar modificaciones. Lo mismo con el almacenamiento de archivos, no hay necesidad de abrir la consola de AWS y tener que buscar manualmente. Todo se encuentra en un solo lugar.
En conclusión, la combinación de la CLI de Amplify junto con la herramienta Visual de Studio Code nos permitió ahorrar horas de trabajo en modelado e implementación de nuestro modelo de datos. Así mismo, nos facilitó la administración y moderación de todo el contenido que fue producido y almacenado en el backend de Amplify.
Ventajas
Tener un servicio como AWS Amplify que despliegue y administre el backend, que se integre con el repositorio git donde alojamos el código fuente del frontend y lo despliegue. Además, que segmente ambientes de desarrollo, producción, etc. Nos ha permitido ahorrar tiempo en tareas administrativas de la aplicación y de levantamiento de infraestructura.
Además, el utilizar una arquitectura completamente sin servidor (Serverless), hace que en el modelo de responsabilidad compartida, se pueda delegar una gran parte de las responsabilidades a AWS y facilite la operación de la solución. Incluso, logramos una gran eficiencia en costos: Ejecutar esta arquitectura para aproximadamente 200 usuarios a lo largo de un mes costó menos de $5 USD.
De cara al frontend, nuestro desarrollo fue muy ágil al poder reutilizar los componentes tanto de CloudScape Design System como de Amplify UI. De esta manera, no nos tenemos que preocupar por el estilizado de la página o del “responsive design” (La capacidad de una página web para adaptarse a distintos tamaños de pantallas/ventanas) y enfocarnos en la lógica del código.
Por último, a través de herramientas como Amplify Studio, el proceso de diseño del backend se simplifica. A través del modelado de datos y la configuración de los componentes logramos ser más efectivos. Como por ejemplo: Crear la política de contraseñas para los usuarios desde la interfaz de autenticación. Además, para hacer actualizaciones en el modelo de datos, solo es necesario modificar el modelo en Studio. Amplify hace el resto del trabajo a la hora de desplegar la nueva versión del backend.
Consideraciones
Si bien, está las decisiones tomadas de arquitectura y elección de tecnologías nos permitió avanzar, tambien hay que mencionar consideraciones que deben ser tomadas en cuenta, por ejemplo:
SQL vs NoSQL
Dadas las relaciones que hay entre las diferentes entidades, el usar una tabla de Amazon DynamoDB que permite emular un funcionamiento de una base de datos relacional, implica usar tablas de hecho para evitar sobre excepciones de aprovisionamiento, y para obtener información relacionada de los registros entre las distintas tablas se debería hacer escaneo en algunas de ellas en algunas ocasiones.
Coherencia de lectura
Dada la naturaleza de la aplicación, era necesario leer y escribir datos dentro de forma fuertemente consistente. Esto representó un desafío para nosotros. Sin embargo, hay aplicaciones que no tienen estos requerimientos y por ende no necesitan esta consideración.
El tipo de coherencia de lectura que usa Amplify al momento de crear un recurso en Amazon DynamoDB, es coherente posterior, el predeterminado. Esto puede hacer que dos usuarios que leen el registro con el id xxx-xxxx tengan una posibilidad de que los valores retornados sean distintos (por alguna actualización preliminar por ejemplo). Esto implicaría un desafío de idempotencia, o de manejo en la aplicación. En el caso de que ese registro devuelto por el backend sea usado para una operación que deba cumplir las reglas ACID (Atómico, Consistente, Aislado y Durable por su traducción de la sigla al español), el resultado podría ser diferente para ambos usuarios y su registro no sería el correcto. Esto se puede solucionar haciendo una correcta administración de los flujos de trabajos con transacciones de DynamoDB.
Conocimiento del funcionamiento de Amazon DynamoDB
Una base de datos de llave-valor como lo es Amazon DynamoDB, utiliza las llaves primarias como identificadores de registro al momento de hacer escrituras o lecturas. Cuando se usa este recurso para emular relacionamiento y realizar análisis de los datos, hace que sea necesario un conocimiento adecuado de los indices de las tablas y de la elección correcta de la llave primaria (llave de partición y una llave de ordenamiento) [1]. Esto con el fin de minimizar la sobrecarga en las tablas y distribuir de forma adecuada la escritura/lectura en las particiones que componen una tabla. El ejercicio correcto de diseñar estas llaves va a permitir que la tabla escale en tamaño sin comprometer el rendimiento de la misma.
Análisis de tablas
El servicio de Amazon DynamoDB en conjunto con otros servicios de AWS puede ser usado para realizar tareas de analítica, como por ejemplo, la combinación de DynamoDB streams con Kinesis Data Stream para captura y/o cambio de los datos. Sin embargo, hay que tener en cuenta que todos los registros se encuentran en distintas tablas. Las operaciones más usadas para hacer agregaciones de los datos, requieren leer todos los datos que se encuentran en las distintas tablas, lo cual resulta siendo más costoso [2].
Recomendaciones
Al momento de implementar un sistema, como el que se expone en este blog, es necesario tener en cuenta aspectos fundamentales antes de ir a producción: Para poder soportar una aplicación con una transaccionalidad moderada y teniendo una base de datos como Amazon DynamoDB, hay que tener en consideración el tipo de operaciones de escritura y lectura. Elegir entre los tipos eventualmente consistente o fuertemente consistente, es un factor clave a la hora de tener implementado ACID en nuestra base de datos y sobre todo los costos.
Por otra parte, el manejo adecuado de indices de Amazon DynamoDB nos permite tener un mejor acceso a los datos. También, es importante considerar los limites de algunos servicios por defecto que usa AWS Amplify, como lo es el límite para envío de correos electrónicos de validación de usuarios de Amazon Cognito.
En cuanto a la auditoria, es importante tener en cuenta qué todas las acciones que pasen en nuestra aplicación deberían poder ser registradas para una posterior auditoria. Amazon Cloudwatch, es un servicio que nos ofrece esas capacidades de registros de logs, para monitorear los recursos y las aplicaciones que se ejecutan en AWS. Por otro lado, AWS X-Ray es un servicio que recopila datos sobre las solicitudes que atiende su aplicación y proporciona herramientas para ver, filtrar y obtener información a fin de identificar problemas, errores y oportunidades de optimización. Esto nos permite llevar esa trazabilidad que necesita nuestra aplicación.
Conclusión
A partir de la elección de AWS Amplify y Cloudscape Design System, nos fue posible levantar una aplicación full-stack de ceros en tan solo 3 semanas. Estas herramientas no solo nos ayudaron a ser lo más ágiles posibles en el proceso de desarrollo e implementación, también nos permitieron operarla de la forma más eficiente y menos demandante. Por último, la aplicación logró soportar 200 usuarios a lo largo de un mes, pero esta misma arquitectura es capaz de soportar miles e incluso millones de usuarios en un futuro.
Ahora que conoce estas herramientas que AWS tiene a disposición, lo invitamos a que pruebe y experimente con ellas. La curva de aprendizaje es muy rápida y el impacto positivo que puede llegar a causar en su negocio en poco tiempo puede ser significativo! Esperamos haya disfrutado del blog y que se divierta construyendo sobre AWS.
Referencias
- https://thinkwithwp.com/es/blogs/database/choosing-the-right-dynamodb-partition-key/
- https://docs.thinkwithwp.com/es_es/amazondynamodb/latest/developerguide/Scan.html
Sobre los Autores
Juan Nicolas Bolaños es Arquitecto de Soluciones en Amazon Web Services para Sector Público. Nicolás trabaja para la vertical de salud con pagadores, prestadores del servicio, farmacias y entidades gubernamentales para la salud. Le apasiona el desarrollo de software, creación de soluciones SaaS, uso tecnologías serverless y aplicaciones en campos disruptivos como la IA generativa. | |
Juan Miguel Bermudez Mieles es Arquitecto de Soluciones en Amazon Web Services para Sector Público. Juan Miguel apoya a distintas entidades e instituciones públicas en Centro América y el Caribe en la adopción de nuevas tecnologías y prácticas que permitan el mejoramiento de sus servicios. |
Sobre los revisores técnicos
Kevin Cortes Rodriguez es Arquitecto Senior de Soluciones en AWS e ingeniero en informática graduado del ITBA, Buenos Aires, Argentina. Trabaja asistiendo y apoyando a los clientes de Gobierno en sector público en su “viaje a la nube” de AWS, trabajando en proyectos que involucran arquitecturas escalables y serverless. Tiene gran interés en las áreas de desarrollo mobile, web, IOT y analítica de datos. | |
Mauricio Romero es Arquitecto de Soluciones Sr. en Amazon Web Services para Sector Público. Mauricio ha trabajado en diferentes proyectos de gobierno electrónico, transformación digital y adopción ágil y segura de nuevas tecnologías en entidades de gobierno, ciudades y empresas de servicios pùblicos. | |
Reynaldo Hidalgo es Arquitecto de Soluciones Sr. en Amazon Web Services para la región Caribe. Reynaldo lideró el Bootcamp de AWS Well-Architected Framework (WAF) para socios de AWS y trabaja con la vertical financiera. Reynaldo viene con más de 15 años de experiencia en desarrollo de software, bases de datos e inteligencia empresarial, infraestructura de telefonía/centros de llamadas y aplicaciones en tiempo real en las industrias financiera y minorista. |