Especificación+AsyncAPI

Especificación AsyncAPI



  • Introducción

  • Similitudes con OpenAPI

  • Estructura

  • Ejemplo de uso

    • Agregar información sobre la aplicación

    • Identificar los servidores

    • Añadir tipo de comunicación

    • Definición de los canales de comunicación

    • Definición de los componentes

    • Especificación completa

  • Herramientas

  • Conclusiones

Introducción

AsyncAPI es una iniciativa open source que trata de buscar una mejora en el estado actual de las Arquitecturas orientadas a eventos (Event-Driven Architecture, EDA). Su propósito a largo plazo es que sea tan fácil de integrar como las API REST, desde la generación de código, hasta el descubrimiento y gestión de eventos. Como bien dicen en su documentación, actualmente la mayoría de procesos donde podemos aplicar API REST deberían ser aplicables también con una arquitectura event-driven.

Supone una solución para automatizar y formalizar la documentación y generación de código de los servicios basados en eventos, así como establecer estándares de calidad y mejorar el gobierno de las APIs asíncronas.

Similitudes con OpenAPI

En Minsait estamos familiarizados con OpenAPI dado que es el estándar que seguimos a la hora de definir nuestras APIs, es por ello que encontraremos muchas similitudes con AsyncAPI ya que esta última parte de la anterior.

Entre sus similitudes podemos destacar las siguientes:

  • Se basan en el principio contract-first, que consiste en crear la definición de la API antes del desarrollo de la misma. Dedicándole tiempo previo a cómo va a usarse la API y a su definición evita problemas futuros que pueden aparecer durante la fase de implementación.

  • Tienen un esquema muy similar, ya que parten de la misma base. Además los esquemas de AsyncAPI son compatibles con los de OpenAPI.

  • Poseen mecanismos de seguridad tales como: usuario y contraseña, certificados, encriptación end-to-end, autenticación HTTP, SASL, API keys y Oauth 2.

  • Presentan una gran cantidad de herramientas muy útiles de cara a su uso, tales como: generadores de documentación y de código, herramientas de desarrollo, frameworks, generadores de mock y testing y validadores.

En la siguiente figura podemos observar las similitudes y diferencias estructurales que nos presentan ambas opciones.

Al margen de las similitudes, que se ven a simple vista en la figura anterior, podemos encontrar ciertos cambios lógicos al tratarse de un tipo de arquitectura diferente:

  • En lugar de definir Paths, se definen Channels o canales de comunicación, que es donde se indican las operaciones de publicación y suscripción.

  • En lugar de tener un esquema de Request-Response, se define uno de publicación y consumo de mensajes identificando las cabeceras y el payload. Es interesante conocer las peculiaridades que nos ayudan a identificar un mensaje que nos interesan consumir en nuestra aplicación y gracias a estos atributos tenemos información sobre ello, pudiendo consumir un mensaje con toda la información indicada o solo parte de ella.

  • Se puede observar también un cambio en los componentes, añadiendo muchos de utilidad que ayudan a la trazabilidad e identificación de mensajes dentro de nuestro sistema.

Estructura

La estructura principal de AsyncAPI es la siguiente:

  1. Info: contiene metadatos acerca de la API, que pueden ser usados por el cliente si fuera necesario. Los datos que contiene son: versión, título, descripción, términos de servicio, datos de contacto y licencia.

  2. Servers: es un listado de servidores, y cada uno representa un broker. En cada broker se especifica principalmente la url y el protocolo soportado para la comunicación, pero también se pueden añadir parámetros como una descripción, seguridad y bindings.

  3. Channels: es un conjunto de Channel individuales. También son conocidos como topics, routing keys, event types o paths. Aquí se define el tipo de operación que hace cada canal, si es una suscripción o una publicación de la información, definiendo como los mensajes son enviados y recibidos.

  4. Components: contiene un conjunto de objetos reutilizables para diferentes aspectos de la especificación, al igual que ocurre con OpenAPI. Sirve para definir esquemas, mensajes y traits, entre otros.

Se recomienda una lectura a su documentación para profundizar mucho más y ver todos los tipos de especificaciones posibles.

Ejemplo de uso

En esta sección vamos a definir un ejemplo simple de una aplicación con EDA para el registro de un usuario al que posteriormente se le va a notificar vía email/telefónica que su registro ha sido completado. Idealmente estos serían dos servicios distintos consumiendo un mismo topic, no obstante para simplificarlo se pondrá en una misma definición con objeto de ver tanto la publicación como la suscripción de mensajes.

Para ello necesitamos completar la estructura del apartado anterior. Así pues, vayamos por partes.

Agregar información sobre la aplicación

Esta parte aunque es simple es muy importante para mantener la información básica de la aplicación y su versión.

Definición de Info de AsyncAPI

asyncapi: '2.2.0' info: title: User SingUp Simple API version: 1.0.0 description: | This service is an example of a simple user SignUp with notifications when the user has signed up in the application. license: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0.html

Identificar los servidores

En este caso vamos a identificar el servidor usando Kafka, pero esto podría ser más generalista o se podría usar otro tipo de bróker de mensajería.

Definición de Servers de AsyncAPI

 

servers: local-server: url: localhost:{port} protocol: kafka description: Apache Kafka Broker server using docker variables: port: default: '9092' description: Broker server port

Como podemos observar hemos definido un servidor local usando el protocolo kafka, y cuyo puerto puede ser variable y es por defecto el 9092.

Añadir tipo de comunicación

Con motivo de evitar tener que definir el tipo de comunicación que se realiza en los mensajes se puede definir de forma general al mismo nivel que la estructura principal de la especificación.

Definición de Content-Type en AsyncAPI

defaultContentType: application/json

El valor que se indique debe ser obligatoriamente un tipo de medio (media type) que en este caso es JSON.

Definición de los canales de comunicación

Esta parte de la especificación se centra en definir los canales de comunicación del servicio que se está definiendo, en este caso vamos a definir un topic de Kafka, un publicador y un suscriptor a ese mismo topic.

Definición de Channels en AsyncAPI

 

Como podemos observar se ha definido un topic llamado user/signup que define una operación de publicación y otra de suscripción. Las dos operaciones están identificadas por un trait que se define posteriormente en la sección de componentes y por un identificador que añade información para la trazabilidad. Además el mensaje que se publica y se consume es el mismo en este caso.

Se hace uso de referencias a partes de la misma definición que se verán posteriormente para darle mayor legibilidad al archivo completo.

Definición de los componentes

Ahora solo hace falta definir los componentes necesarios para esta aplicación en concreto, que son los mensajes, los esquemas y los traits que estamos usando para la trazabilidad.

Definición de Components en AsyncAPI

 

 

Se ha definido el mensaje UserSignUp, con información básica para su uso y haciendo uso del esquema UserSignUpPayload. Este último define las propiedades del mensaje que se va a propagar por nuestro bróker de mensajería, siendo un objeto JSON con los siguientes atributos:

  • name, para definir el nombre del usuario.

  • email, para definir el email del usuario. Tiene el formato email que generará los ejemplos con un formato de email válido.

  • phoneNumber, para definir el número de teléfono del usuario. Añade además una expresión regular para este campo que se corresponde con el número de teléfono internacional.

Por último se especifican los traits, y en nuestro caso hacemos uso de los bindings de Kafka para especificar el grupo al que pertenecen los mensajes que incluyan esta propiedad. Se puede encontrar más información sobre los distintos bindings en el GitHub de AsyncAPI.

Especificación completa

Especificación completa AsyncAPI

 

 

Herramientas

Como ya anticipábamos previamente, AsyncAPI ofrece una gran cantidad de herramientas de apoyo a la generación de código, documentación, mocking.., etc.

Para la generación de documentación podemos usar la herramienta online o integrar la herramienta en nuestros pipelines de forma que generará la definición en formato HTML en un formato amigable y puede ser desplegado para la consulta dentro de nuestra infraestructura.

Las herramientas de generación de código aún necesitan tiempo de maduración con respecto a las previamente vistas, pues no incluyen todas las especificaciones añadidas. No obstante ofrecen un esqueleto funcional de una aplicación (en este caso hemos probado con Java).

Generación de código Java con @asyncapi/generator

 

 

También existen herramientas que generan la documentación a partir del código, como por ejemplo la conocida SpringFox para OpenAPI, y disponibles para varios lenguajes como Go, C#, Java, Kotlin, Scala y JavaScript.

Conclusiones

En la actualidad en muchos proyectos con una arquitectura orientada a eventos todo el peso de documentación, generación de código, definición de la estructura de datos para ser usada en el ecosistema de la aplicación, validación.., etc, lo tiene el equipo de desarrollo. Cuando estos proyectos son a gran escala puede suponer un problema, y la aparición de AsyncAPI da respuesta a este contratiempo.

En primer lugar, es importante hacer hincapié en que para equipos que estén habituados en el uso de OpenAPI saltar a esta nueva especificación va a resultar un proceso bastante cómodo, ya que parten esta última.

En segundo lugar, esta alternativa está próxima a convertirse en un estándar en la definición de aplicaciones con EDA, no obstante está en proceso de maduración ya que muchas de las herramientas que proporciona están algo incompletas y en proceso de crecimiento. Utilizar AsyncAPI a día de hoy cobra bastante sentido para documentar los sistemas EDA, ya que permite mantener la coherencia y eficiencia que puede perderse en caso de no usarse.

En definitiva, actualmente AsyncAPI es bastante útil para documentar sistemas basados en eventos y de esta forma mantener una gobernanza coherente de estos sistemas. No obstante, aún le falta mucho por crecer para llegar al nivel de OpenAPI y su facilidad de integración, pero está próximo a serlo no solo por el sentido que tiene usar esta alternativa, sino por la extensa comunidad que tiene detrás trabajando para mejorar la calidad de sus herramientas y definiciones.