REST y el versionado de servicios

0
17949

En este tutorial intentaremos explicar cómo versionar correctamente un servicio en función de los cambios que se produzcan en su API, cuándo los cambios son compatibles hacia atrás y el proceso completo de versionado.

0. Índice de contenidos.


1. Introducción

Ya tuvimos la ocasión de hablar en múltiples tutoriales sobre arquitecturas orientadas a servicios o arquitecturas basadas en REST. Existe un problema muy común en estos modelos de arquitectura que es el cambio del contrato de nuestros servicios, cuando un servicio, por una determinada circunstancia, cambia su API y afecta a los diferentes consumidores del mismo. ¿Sabemos gestionar correctamente dicho cambio de contrato?, ¿cómo actuamos ante esta situación?.

En este tutorial intentaremos explicar cómo versionar correctamente un servicio en función de los cambios que se produzcan en su API, cuándo los cambios son compatibles hacia atrás y el proceso completo de versionado.


2. Entorno.

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro Retina 15′ (2.2 Ghz Intel Core I7, 16GB DDR3).
  • Sistema Operativo: Mac OS Yosemite 10.10


3. El problema.

Supongamos que tenemos un caso como el que se representa en la siguiente figura.

El problema

Si nos fijamos, contamos con un servicio que es consumido por diferentes clientes. A priori, es un escenario ideal en cualquier arquitectura distribuida ya que nuestro servicio es consumido por diferentes clientes y, por tanto, está siendo reutilizado.

Hasta aquí todo va perfecto pero, ¿qué sucedería si cambiase el API (contrato) de nuestro servicio?

Cambio API

La primera solución que se nos puede venir a la cabeza podría ser realizar una subida sincronizada a producción entre el servicio y todos sus consumidores. Sin embargo, esta acción puede ser demasiado arriesgada e incluso inviable en muchas ocasiones. Podríamos destacar los siguientes inconvenientes:

  • Necesidad de un plan de contingencia por si algo falla. Marcha atrás.
  • Es una subida a producción muy compleja.
  • Casi inviable cuando los clientes afectados son dispositivos móviles.
  • Si un punto falla, se revierte todo.
  • Es posible que uno o varios consumidores no puedan migrarse en ese momento.

Y entonces, ¿existe alguna solución? Por supuesto, el versionado de servicios… 🙂


4. ¿Por qué versionar servicios?.

El versionado de servicios es una práctica por la cual, al producirse un cambio en el API de un servicio (no tiene por qué ser únicamente un API REST), se libera una nueva versión de ese servicio de manera que la versión nueva y la anterior conviven durante un periodo de tiempo.

De esta manera, los clientes se irán migrando a la nueva versión del servicio de manera secuencial. Cuando todos los clientes estén consumiendo la última versión del servicio, se retira la anterior.

Gráficamente podríamos representarlo de la siguiente forma:

Antes de nada lo que haremos será asegurarnos de que versionamos de manera independiente el API de un servicio y su correspondiente servicio de backend (lógica funcional que encapsula).

Versionamos API

Cuando se produce un cambio en el API del servicio, lo siguiente que hacemos es liberar una nueva versión del servicio con su correspondiente nueva versión del API y lógica de backend. Una vez liberada la nueva versión, avisamos a los consumidores del servicio indicando que existe una nueva versión disponible.

Liberamos nueva versión

Una vez dado el aviso a los consumidores, éstos se van adaptando para consumir la nueva versión del servicio.

Migración - Paso 1

Poco a poco se van migrando la mayoría de los consumidores.

Migración - Paso 2

Y llega un punto en el que todos nuestros consumidores están consumiendo la última versión del servicio.

Migración - Paso 3

Una vez que ya tenemos a todos los consumidores utilizando la última versión del API del servicio, retiramos la versión antigua.

Migración - Retirada

¿Y por qué todo esto? Pues muy sencillo, porque los servicios cambian, es inevitable. Cambia el contrato (API) y cambia la implementación. El objetivo de todo esto es minimizar el impacto del cambio del contrato.

Para que los clientes puedan distinguir entre una versión u otra del servicio, se suelen utilizar principalmente dos técnicas (aunque sobre esto suele haber muchísimo debate):

http://host:puerto/api/v1/recurso

  • Añadir un parámetro en la cabecera de la petición que indique la versión del API que queremos consumir.
URL: http://host:puerto/api/recurso
Method: GET
Headers:
	api-version: v1

Por otro lado, la idea de que convivan dos instancias de un mismo servicio (dos war, ear, o lo que sea…) y no añadir la funcionalidad de las dos versiones en una misma instancia es, lógicamente, por temas de mantenibilidad del servicio.


4.1. Los cambios retrocompatibles.

No todos los cambios en el API tienen impacto sobre los consumidores de la misma (al menos, no deberían tenerlo). A estos cambios se les suele denominar cambios retrocompatibles. En caso de que nuestra API sufra cambios de este tipo no debería ser necesario liberar una nueva versión, bastaría con reemplazar la actual. Lo que si sería muy conveniente es avisar a nuestros consumidores con los nuevos cambios para que los tengan en cuenta.

Este es un listado de cambios en un API que NO deberían afectar a los consumidores:

  • Añadir nuevas operaciones al servicio. Traducido a REST sería añadir nuevas acciones sobre un recurso (PUT, POST…)
  • Añadir parámetros de entrada opcionales a peticiones sobre recursos ya existentes. Ej: un nuevo parámetro de filtrado en un GET sobre una colección de recursos
  • Modificar parámetros de entrada de obligatorios a opcionales. Ej: al crear un recurso, una propiedad de dicho recurso que antes fuese obligatoria y que pase a opcional.
  • Añadir nuevas propiedades en la representación de un recurso que nos devuelve el servidor. Ej: ahora a un recurso Persona que anteriormente estuviese compuesto por DNI y nombre, le añadimos un nuevo campo edad.

Este otro listado muestra cambios que SI deberían afectar a los consumidores:

  • Eliminar operaciones o acciones sobre un recurso. Ej: Ya no se aceptan peticiones POST sobre un recurso.
  • Añadir nuevos parámetros de entrada obligatorios. Ej: ahora para dar de alta un recurso hay que enviar en el cuerpo de la petición un nuevo campo requerido.
  • Modificar parámetros de entrada de opcional a obligatorio. Ej: ahora al crear un recurso Persona, el campo edad, que antes era opcional, ahora es obligatorio.
  • Modificar un parámetro en operaciones (verbos sobre recursos) ya existentes. También aplicable a la eliminación de parámetros. Ej: al consultar un recurso ya no se devuelve determinado campo. Otro ejemplo: un campo que antes era una cadena de caracteres, ahora es numérico.
  • Añadir nuevas respuestas en operaciones ya existentes. Ej: ahora la creación de un recurso puede devolver un código de respuesta 412.

Estos listados son orientativos e identficados en base a mi experiencia pero pueden existir otro tipo de cambios así que tampoco hay que tomárselo al pie de la letra.


5. El proceso.

Haciendo un resumen de lo que hemos visto en este artículo, el proceso de versionado de servicios podría representarse gráficamente de la siguiente manera:

El proceso

  • Si existen cambios en nuestro servicio que NO afectan al API, únicamente reemplazamos el servicio actual. Ejemplo: solucionamos un error.
  • Si los cambios afectan al API, evaluamos si son retrocompatibles.
    • En caso de cambios retrocompatibles: avisamos a los consumidores con los nuevos cambios y reemplazamos el servicio actual.
    • En caso de cambios NO retrocompatibles: avisamos a los consumidores y liberamos una nueva versión del servicio.
      • Cuando todos los clientes estén utilizando la nueva versión, retiramos la antigua.

6. Referencias.


7. Conclusiones.

El versionado de servicios es una actividad crítica, no sólo en arquitecturas orientadas a servicios, sino siempre que trabajemos con servicios (REST, SOAP…) que sean consumidos por diferentes clientes. Su objetivo es minimizar el impacto del cambio del contrato habilitando una migración secuencial entre nuestros consumidores.

El versionado de servicios es una solución relativamente sencilla de ejecutar y que nos solucionará bastantes problemas a la hora de gestionar el ciclo de vida de los servicios. Además es una labor imprescindible en la fase de diseño de nuestro gobierno de servicios, aunque este ya sería otro tema que da para varios tutoriales… 🙂

Espero que este tutorial os haya sido de ayuda. Un saludo.

Miguel Arlandy

marlandy@autentia.com

Twitter: @m_arlandy