El monolito
A la hora de
desarrollar una nueva aplicación, independientemente de la arquitectura
software que utilice, es hacerlo todo en un mismo paquete. Este paquete incluye
todos los módulos que requiere la aplicación y su despliegue se hace como
paquete lo que se denomina comúnmente monolito.
Los monolitos son
extremadamente comunes dado que inicialmente son relativamente simples de
desarrollar, testear y desplegar. Muchas herramientas de desarrollo están
orientadas a desarrollar este tipo de aplicaciones monolíticas. A la hora de
escalar sólo hay que ejecutar varias instancias detrás de un balanceador de
carga.
En fases tempranas
de un proyecto esta estrategia funciona bien.
El camino hacia el monolito infernal
Si la vida de la
aplicación se empieza a alargar, el equipo de desarrollo irá añadiendo cada vez
más código al monolito. Después de una tiempo el pequeño monolito se empezará a
convertir en un pequeño monstruo el cual, sin las medidas oportunas, se
convertirá en un monolito infernal.
Una vez se ha
llegado al tener un monolito infernal los intentos de hacer un desarrollo o
despliegue ágiles serán cada vez más difíciles. Otro de los mayores problemas
es que la aplicación es demasiado grande para que un sólo desarrollador pueda
entenderla completamente. Esto conlleva a que el desarrollo de nuevas funcionalidades
sea cada vez más complejo dando lugar a una espiral de dolor dando lugar a una
“big ball of mud”.
Otro problema del
monolito infernal es que es un obstáculo al despliegue continuo. El estado del
arte es que las software como servicio (SaaS) despliegue los cambios a
producción varias veces al día y esto, es extremadamente difícil con un
monolito complejo desde que hay que desplegar la aplicación entera para
actualizar cualquier parte de ella.
Como el impacto de
los cambios no es tarea fácil ya que no se llega a entender bien hay mucha
probabilidad de tener que hacer las pruebas manualmente con lo será
prácticamente imposible hacer el despliegue continuo.
La escalabilidad
también puede verse comprometida en aquellos casos en los que diferentes
módulos requieren de recursos específicos. Por ejemplo, un módulo puede
requerir un uso intensivo de procesamiento mientras que otro requiere una gran
cantidad de memoria.
Otro problema
inherente a la naturaleza del monolito es la fiabilidad. Como todos los módulos
se ejecutan en el mismo proceso, en el momento que haya un bug puede romper el
proceso.
Otro aspecto
importante es el acoplamiento que hay en las tecnología en uso dado que
reescribir la aplicación puede llegar a ser extremadamente costoso.
¿Cómo abordar el problema?
El patrón de
arquitectura de microservicios define una arquitectura de servicios donde estos
servicios colaboran entre sí y no están acoplados entre ellos.
Un servicio
implementa un conjunto de funcionalidades que tienen relación como puede ser la
administración de pedidos, la administración de clientes, etc. Cada servicio es
una aplicación con su propia arquitectura hexagonal con su lógica de negocio
con los adaptadores necesarios.
Como estos
servicios contienen partes de la aplicación se denominan microservicios.
Cada servicio de
backend expone una API REST con la que otros servicios se pueden comunicar. La
comunicación entre los servicios también puede ser asíncrona como la
comunicación basada en mensajes.
No obstante, las
distintas aplicaciones que utilizan estos servicios de backend no deberían
hacerlo directamente si no a través de un intermediario conocido como API
Gateway.
La Api Gateway se
responsabiliza de tareas como el balanceo de carga, caché, control de acceso,
métricas de API y monitorización.
Otro aspecto
importante es que la arquitectura de microservicios impacta en la base de datos
de manera que ya no hay una única base de datos si no que cada servicio tiene
su propia base de datos. Esto es esencial aunque a veces resulte en duplicación
de algunos datos para evitar el acoplamiento.
Esta estrategia
tiene otro beneficio añadido y es que cada servicio puede utilizar el tipo de
base de datos que más se ajuste a sus necesidades.
La arquitectura de
microservicios es similar a SOA en cuanto a que ambas consisten en un conjunto
de servicios aunque SOA tienen los Web Service Especifications (WS-*) y los
Enterprise Service Bus (ESB) mientras que los microservicios usan protocolos
más simples como REST. Los microservicios también rechazan el concepto de
esquemas canónicos para la base de datos.
Los beneficios de los microservicios
El primer
beneficio es que ataca al problema de la complejidad descomponiendo una
aplicación monolítica monstruosa en servicios siendo cada uno de estos mucho
más manejables.
El segundo
beneficio es que desarrollo independiente de cada servicio con lo que son mucho
más rápidos de desarrollar. Un equipo de desarrollo puede enfocarse en el
desarrollo de este servicio utilizando las tecnologías que se adecuen mejor a
su propósito.
El tercer
beneficio es que el servicio se puede desplegar independientemente por lo que
no se necesita una comunicación con otros equipos. Tan pronto como el
desarrollo está probado se puede desplegar con lo que el continuous deployment
es posible.
El cuarto
beneficio es la escalabilidad de los distintos servicios de manera
independiente. Se pueden utilizar servidores que se adecuen a sus los
requisitos de recursos como pueden ser capacidad de procesamiento,
almacenamiento o memoria.
Las desventajas de los microservicios
No es oro todo lo
que reluce y, como bien apuntó Fred Brooks en su libro “The Mythical
Man-Month”, no hay balas de plata.
La primera
desventaja es el propio término “microservicio” el cual parece hacer excesivo
énfasis en el tamaño de un servicio pero sin definir cuán de pequeño debe ser.
Al ser interpretable cada desarrollador puede llevarlo al extremo que quiera
creando servicios de 10 líneas, 100 líneas, 1000 líneas, etc. Así que el nombre
es más una directriz a seguir que una reglas fijas. El propósito de los
microservicios es descomponer la aplicación para facilitar el desarrollo, el
despliegue y la escalabilidad.
Otro reto al que
se enfrentan los microservicios es tener el esquema de la base de datos
separado. Cuando se tiene una sóla base de datos las transacciones son algo
trivial sin embargo, cuando se tienen microservicios estos tienen su propia
base de datos. Las transacciones distribuidas no son una opción y no sólo por
el teorema del CAP sino porque cada servicio puede tener bases de datos que no
soporten una transacción. Para abordar este problema habrá que usar una
estrategia de consistencia eventual lo cual será un reto para los
programadores.
La pruebas en los
microservicios también añaden un grado de complejidad dado que probar un
servicio puede requerir lanzar ese servicio a la vez que cualquier servicio del
cual dependa o al menos, lanzar “stubs” para estos servicios. No es que sea
ciencia de cohetes pero hay que tener en cuenta esta complejidad extra.
Otro reto al que
deben enfrentarse los desarrolladores es es implementar cambios que impacten en
múltiples microservicios. Por ejemplo, imaginemos que hay que implementar una
historia que requiera cambios en los servicios A, B y C donde A depende de B y
B de C. En este caso hay que coordinar los cambios de cada servicio de manera
los cambios se deberán abordar de manera inversa. Primero habría que actualizar
el servicio C, después el servicio B y finalmente el servicio A.
El despliegue de
los microservicios también es mucho más complejo. Una aplicación basada
en microservicios contiene un número importante de servicios que los
cuales deben ser configurados, desplegados, escalados y monitorizados.
Además, se debe
implementar un “service discovery mechanism” que habilita un servicio para
descubrir las localizaciones (host y puerto) de cualquier otro servicio que
necesite comunicarse con él. Esto lleva a que se requiera de un gran control en
el despliegue así como en la automatización de los despliegues.
Una estrategia
para la automatización es usar “Platform as a service” (PaaS). Un típico punto
de comienzo es usar una solución de clustering como Kubernetes en conjunción
con una tecnología de contenedores como Dockers.
Resumen
La construcción de
aplicaciones complejas es inherentemente difícil. El patrón de arquitectura de
monolito tiene sentidos para aquellas aplicaciones simples. A medida que el
monolito comienza a crecer se volverá más complejo hasta que casi sea
impracticable acometer los cambios.
La arquitectura de
microservicios, a pesar de sus desventajas y los retos de implementación, es
una mejor elección para aquellas aplicaciones que van a a ser complejas.
No hay comentarios:
Publicar un comentario