El uso de arquitecturas serverless ha venido en aumento, ya he hablado un poco de ello, además soluciones propias de la nube como por ejemplo las lambdas de AWS escalan increíblemente rápido, como se aprecia en la Figura 1 es una muestra de como se puede escalar a 3.500 instancias casi de forma inmediata, y esto claramente sin realizar ninguna configuración, este tipo de soluciones donde el foco está centrado en cumplir los objetivos del negocio y delegar la administración de la infraestructura a AWS, es un enfoque con bastante hype, sin embargo, no siempre la mejor solución está en el camino de las arquitecturas serverless, por eso en este artículo se dará inicio a una serie de más publicaciones, en donde comento, en mi opinión cuáles si son casos ideales para utilizar esta arquitectura, y hoy empezaré con event driven

Figura 1: Escalamiento de diferentes tecnologías, obtenido de https://www.vladionescu.me/posts/scaling-containers-on-aws-in-2022/ 

Event driven architecture

Este es una arquitectura en donde su objetivo principal es desacoplar la comunicación entre diferentes microservicios, convirtiendo la comunicación entre los componentes a un enfoque asíncrono, en donde existen consumidores y productores de eventos, logrando con ello evitar varios problemas que surgen de la comunicación síncrona, como por ejemplo

  • Acoplamiento entre otros servicios: el Microservicio A invoca B, el B invoca C, el C invoca D  y así sucesivamente
  • Captura de fallos: tener una lógica para capturar cada fallo que genera cada invocación con enfoque request-response
  • API Versioning: el método más común es utilizar RESTFULL por medio de HTTP y esto está limitado por las versiones o cambios que puede tener su respuesta

además de todos ellos, el más costoso es quizás hacer muchas cosas en una misma transacción, ya que toda invocación, y sobre todo las  que implican salir o viajar por red suma ms, y esto es superimportante, porque el tiempo de respuesta en muchas empresas es algo crucial, incluso es parte su promesa de valor, para ello revisemos un ejemplo que nos dé un poco más de contexto:

  • supongamos que debemos trabajar en un servicio que sé encargara de registrar unos clientes, este servicio tiene unos componentes sencillos como se ven continuación
Figura 2: Componentes de la arquitectura

pero ocurre un cambio y ahora se desea que además de registrar el cliente se envíe un correo notificándolo del registro, estas son las  opciones que tendríamos

  • La información del envío del correo la tiene el Microservice B, fácilmente se puede optar por que en este servicio se implemente el envío de correo en la misma transacción, es decir, incluir la implementación en un módulo dentro de ese servicio
  • La segunda opción es optar por separarlo en un servicio aparte, pero esto genera ahora que también exista una  implementación parcial en el Microservice B porque debe invocar al servicio que se encarga de enviar los correos
Figura 3: Opciones de implementación

las dos opciones descritas anteriormente, estarían encontrá vía del principio de la responsabilidad única, debido a que el Microservice B debe implementar una lógica para el envío del correo y aunque en la opción dos es más liviana, pueden ocurrir mil y una situaciones que deben tenerse en cuenta para garantizar la continuidad del flujo, de esto se habla más profundidad en el libro Domain-Driven Design, aquí en este contexto es donde se ve una oportunidad para event driven architecture, ya que el envío de correo puede y debería ser asíncrono, en las siguientes Figuras se puede apreciar que componentes intervienen en este nuevo enfoque

Figura 4:Microservice topology solamente stream y microservicios, tomado de https://learning.oreilly.com/library/view/building-event-driven-microservices/
Figura 5: Bussiness topology hibrido, tomado de https://learning.oreilly.com/library/view/building-event-driven-microservices/

La segunda topología suele ser la más común, porque es normal tener una arquitectura hibrida, donde se use procesamiento síncrono mezclado con procesamiento asíncrono, el concepto de event stream es básicamente ese bus de datos en donde viajara la información, al no viajar de forma síncrona tiene que quedar en algún lugar guardado para su posterior consulta, estos buses de eventos además tienen productores y consumidores, es decir servicios que ponen información y servicios que los consumen, este tema da muchísimo para hablar, recomiendo este libro, ahora con esos conceptos claros podríamos tener una tercera opción a nuestra solución del ejemplo anterior, donde se vería más o menos así

Figura 6:  Solución 3 usando event streams

En esta nueva opción de solución, ahora el Microservice B se desentiende totalmente del envío del correo, si falla, si hay varios intentos si el servicio está caído, etc ya no es problema del servicio B, este hace solamente la tarea de guardar en la base de datos, luego a partir de ello se crea un bus de eventos en donde cuando se guarda un dato en la db, este mismo se envía a ese bus de eventos, el cual es consumido en el Microservice C, este servicio tiene toda la lógica de envío de correos, reintentos si el servicio de caído etc., y cuando envíe el correo notifica al bus de eventos que todo funciono, para marcar el stream como exitoso, esta solución aunque es pequeña, puede ser compleja de lograr, sin embargo, gracias al cloud nuestra aplicación puede ser cloud native, y dejarle la complejidad de los stream al proveedor de nube, veamos como AWS maneja estos streams

Ejemplo con streams

Generalmente, administrar estos event streams, se traduce  en un tema de gestionar colas de eventos, hay un montón de tecnologías para hacerlo como por ejemplo apache kafka, cuando estamos trabajando con cloud native, en AWS esta complejidad es administrada como infraestructura, y para ello usaremos un servicio llamado Kinesis, que junto con sus  streams nos permite disparar eventos en la mayoría de sus servicios de almacenamiento de datos que ofrece AWS, uno de ellos es DynamoDB (usaremos DynamoDB en vez de MYSQL), para poder trabajar, se debe crear un stream en kinesis y luego desde DynamoDB conectarlo a este stream

Figura 7:  Configuracion de stream en DynamoDB

luego desde serverless, lanzamos la siguiente configuración


service: myproject

frameworkVersion: '3'

provider:
  name: aws
  runtime: nodejs14.x

functions:
  dynamodb-trigger:
    handler: handler.hello
    events:
      - stream:
          type: kinesis
          arn:
            Fn::Join:
              - ':'
              - - arn
                - aws
                - kinesis
                - Ref: AWS::Region
                - Ref: AWS::AccountId
                - stream/proofconcept
          batchSize: 1
          startingPosition: LATEST
          maximumRetryAttempts: 10
          enabled: true




plugins:
  - serverless-plugin-typescript
serverless.yml

import {unmarshall} from "@aws-sdk/util-dynamodb"

export async function hello(event, context, callback) {

  console.log("invocation")
  console.log(event)
  let record = event.Records[0]
  const payload = record.kinesis
  const data = JSON.parse(Buffer.from(payload.data, 'base64').toString())
  console.log(data)
  console.log(unmarshall(data.dynamodb.NewImage))
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Go Serverless v1.0! Your function executed successfully!',
      input: event,
    }),
  };

  callback(null, response);
}
handler.ts

e insertamos un dato en tabla de DynamoDB

Figura 8:  Insertando un item en DynamoDB

cuando le damos guardar y vemos la consola de logs de la lambda que desplegué, podemos ver como llega la información

Figura 9: Log de la lambda que es invocada por kinesis

Con esta información, puedo enviar el correo usando algún servicio como AWS SES, y cuando se termine de hacer el proceso notificar a kinesis que todo estuvo ok, si hay algún fallo, se debe implementar una lógica de reintentos. Con esto se puede ver un ejemplo muy sencillo de event driven y las razones de que la arquitectura orientada a eventos, es un caso en donde las lambdas encajan perfectamente, pues las lambdas tienen estos puntos de entradas que se conectan nativamente con los stream de kinesis, haciendo la gestión del consumo y la producción de los eventos mucho más fácil

Conclusiones

Event driver  architecture es el caso más claro para saber que se puede utilizar junto con arquitecturas serverless y sobre todo con lambdas de AWS, ya que toda la complejidad de infraestructura que garantiza la comunicación entre microservicios es gestionada por la nube, es importante tener en cuenta que estos servicios deben ser pequeños, pero además es ideal que contengan un bounded context bien definido que le permita manejar lógica de negocio y cambiar al mismo ritmo que el negocio evoluciona, de esto pueden encontrar más en este libro

¿Te gusto el post y quisieras poder aplicar lo aprendido?

Únete al equipo de Simetrik AQUI