Los servicios Back-End deben ser como «legos»


Autor: M. en C.  Alejandro Cruz Rojas    Sígueme en LinkedIn

Introducción

Este artículo es la tercera parte del artículo previo “Diseño de Software as a Service: Los 12 factores” Puede acceder al documento descrito en alguno de los siguientes enlaces:

¿Qué propone el factor 4?

Supongamos, por ejemplo, que tenemos una tienda en línea que integra una pasarela de pagos de un proveedor determinado. Esto nos permite recibir pagos con tarjeta de crédito o débito. ¿Qué pasa con nuestra aplicación si esta pasarela empieza a tener problemas de estabilidad o de seguridad? ¿Y qué sucede si el proveedor de la pasarela súbitamente aumenta los costos de su servicio?

Aplicación que depende de dos servicios especializados

Tenemos ahora un problema derivado de la dependencia crítica con esa pasarela, pero ¿Es el único proveedor de un servicio de este tipo? La respuesta es no. Hay otros proveedores con servicios equivalentes. Claro, cada uno se integra de un modo distinto. Esto significará una barrera de salida dura de pasar. ¿Cuál sería, entonces, una buena estrategia para integrar este servicio con nuestra aplicación?

Deberíamos encontrar la manera de que la integración de un proveedor o de otro, sea muy similar.  De este modo, si uno de esos servicios se vuelve fuente de conflicto podemos cambiarlo por otro equivalente, con relativa facilidad. El concepto de acoplamiento es clave en la estrategia que estamos mencionando.

El factor 4 propone que los servicios que usa el back-end estén desacoplados pero,  ¿Qué significa esto en términos simples? Básicamente, se busca que el diseño del back-end facilite el remplazo, si es necesario, de proveedores de servicios especializados.

¿Qué es el acoplamiento?

Es la medida en la que los componentes de un sistema están interconectados y dependen entre sí. Desde una perspectiva académica tenemos alto y bajo acoplamiento.

  • El acoplamiento alto se da cuando un componente está diseñado para conectarse, exclusivamente, con otro que tiene características muy específicas y por lo tanto no es intercambiable. Por ejemplo, ¿Podemos colocar el cofre de un automóvil Volkswagen sedán en un Nissan Sentra? La respuesta es que no. El cofre del Volkswagen está especialmente diseñado para ser ensamblado con un modelo sedán (incluso puede ser problemático instalar el cofre de un modelo 75 en un Sedán modelo 79). Entonces, podemos decir que el cofre del auto está fuertemente acoplado al sedán.
  • El bajo acoplamiento se da cuando un componente está diseñado para conectarse con cualquier otro que cumpla con determinados requisitos de interconexión. Si regresamos al Volkswagen sedán y ahora pensamos en las llantas que usa ¿Podemos intercambiar llantas con otro u otros modelos de auto, del mismo o de otros fabricantes? La respuesta es sí. Podemos decir que el acoplamiento entre las llantas y el vehículo es bajo. Otro modo de decirlo es que el vehículo está desacoplado de sus llantas.
Alto y bajo acoplamiento

¿Cómo se hacen componentes con bajo acoplamiento?

Para interconectar dos componentes y conseguir que tengan bajo acoplamiento requerimos que:

  • Exista un componente independiente que establezca los requisitos rigurosos (contrato) que cada componente proveedor debe cumplir para interconectarse y colaborar.
  • Existan uno o más componentes proveedores que cumplan, cada uno, con el contrato establecido.
Papel de un contrato
  1. Los componentes con los roles de cliente y de proveedor dependen del contrato.
  2. Cualquier componente dependiente (proveedor) que cumpla con el contrato se puede conectar y puede colaborar con el componente independiente (cliente).

¿Cómo hacer componentes de software que estén desacoplados?

Al diseñar software contamos con las definiciones abstractas (en la mayoría de los lenguajes de programación, clases abstractas o bien, interfaces), un recurso para definir nuestros contratos. Esto le confiere mayor estabilidad: Mientras la abstracción no cambie, las modificaciones en el servicio dependiente no le afectarán.

Considerando nuestro ejemplo de tienda virtual, consideremos un componente llamado “GestorEntregaMercancia”, que gestiona la entrega de la mercancía adquirida por un comprador. Este componente depende de un servicio implementado por algún proveedor de mensajería (los del camión amarillo, o los del camión café, o los de otros colores). El sistema que tiene cada proveedor debe, entre otras cosas:

  1. Tomar los datos del pedido (identificación, medidas, peso, etc.).
  2. Cotizar el costo del envío.
  3. Programar la fecha, hora y lugar en la que se recogerá la mercancía.
  4. Programar la fecha, hora y lugar en la que se entregará esa mercancía al cliente final.
  5. Otorgar un identificador de seguimiento, con el que se pueda monitorear la entrega.

 Todos los proveedores harán las mismas funciones, pero cada uno con sus propios procesos y particularidades. Sin embargo, el componente “GestorEntregaMercancíainvocará siempre a un componente abstracto con una serie de métodos que son constantes y siempre los mismos. En este caso, definidos a través de una interface llamada “IProveedorMensajeria”.

Cada componente de entrega de mercancía, de cada proveedor, debe tener un componente concreto que “implemente” esa interface y que haga la intercomunicación entre nuestro sistema y el de ellos.

Contrato implementado por medio de una interface

Existirá algún componente de infraestructura que “ensamble” al arranque de la aplicación, la definición abstracta “IProveedorMensajería”, que conoce nuestro “GestorEntregaMercancía” con una o más implementaciones ofrecidas por los distintos proveedores. Y es así, que “GestorEntregaMercancía” nunca se enterará con cual de las implementaciones concretas está interactuando.

Entonces, un componente puede invocar y usar la lógica de otros componentes por medio de abstracciones. Esta es una manera eficaz de desacoplarlo de cualquier implementación en particular. De hecho, el componente suele ignorar cual implementación concreta está usando. Esto, debido a que típicamente la implementación concreta se ensambla al arranque de la aplicación y de acuerdo con una  configuración. (Diversos frameworks de desarrollo tienen algún motor de Inyección de dependencias que hace la labor de ensamble utilizando una configuración personalizable).

¿Qué ventajas y desventajas tiene desacoplar servicios?

Dentro de las ventajas, podemos mencionar:

  • Cambiar de un proveedor a otro se vuelve trivial. Se puede hacer desde la configuración o bien se puede implementar un localizador de servicios para hacerlo en tiempo real.
  • Tenemos mayor poder de negociación con los proveedores. Tenemos una estrategia para evitar abusos de parte de proveedores. Si un proveedor abusa, simplemente lo remplazamos por otro de un modo limpio.
  • Se puede gestionar el impacto que un cambio, hecho por un proveedor a su servicio, tiene sobre nuestro software. Consideremos que es fácil detectar cuando se rompe un contrato.
  • Si falla un servicio, podemos remplazarlo por otro de modo inmediato, así logramos que nuestro software tenga mayor resiliencia a fallos de terceros.

Podemos mencionar algunas desventajas (no hay lunch gratis):

  • Requiere más trabajo de diseño y mayor disciplina. Primero, se deberá diseñar el contrato cuidadosamente, luego se deberá diseñar una buena estrategia de pruebas de cualquier implementación y por último, se deberá probar que cada nueva implementación cubra el contrato a nivel de firmas y a nivel de funcionamiento real.
  • En ocasiones requerirá que se programe un adaptador. Los adaptadores son componentes que cumplen con el contrato y que anidan a un componente que es incompatible con el contrato. Su responsabilidad es compatibilizar a un componente con una interface, traduciendo cada interacción, de nuestro formato al formato propietario del componente incompatible y viceversa.

¿Qué reza el factor 4?

Los servicios de Back-End deben estar desacoplados y ser fácilmente «enchufables-desenchufables«. Considere los servicios de los que depende nuestra tienda electrónica. Cada uno representa un riesgo en términos comerciales, operativos y de resiliencia. Definitivamente es sano que diseñemos nuestra aplicación para poder “desenchufar” proveedores que no son adecuados y “enchufar” a mejores opciones. Todo ello, en tiempos mínimos.

Ejemplo de Aplicación que usa diversos servicios de terceros

Conclusión

En la medida que desacoplamos servicios usados por nuestras aplicaciones de back-end, hacemos más robusta a esa aplicación.

El recurso técnico más relevante es la abstracción usada para representar a un contrato. En términos de la mayoría de las plataformas de desarrollo, hablamos de Interfaces y/o de clases abstractas.

Por ahora, hemos comentado ampliamente el factor 4. Continuaremos con los otros factores en entregas futuras… No te las pierdas 🙂

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *