Críticas a la arquitectura microservicios

Es bien conocido por todos nuestros lectores que soy un amante y defensor de la arquitectura microservicios. Desde hace cerca de 5 años estoy bastante involucrado con la implementación de soluciones soportadas en esta arquitectura; con esa experiencia como base, en esta entrada realizaremos una crítica a esta arquitectura. Así como los microservicios son idóneos para muchos escenarios no son la panacea, por lo que saber criticarlos forma parte de nuestra madurez como desarrolladores.

Críticas

Debajo relaciono los puntos de críticas fundamentales que abordaremos en las siguientes secciones.

  • La comunicación entre los servicios tienen un coste superior al de un sistema monolítico.
  • La consistencia, cuando involucra varios microservicios siempre será eventual.
  • Cada microservicios es una barrera de información.
  • Implementar microservicios requiere de equipo maduros.

La comunicación entre los servicios tienen un coste superior al de un sistema monolítico

Los microservicios se ejecutan como piezas independientes en su propio proceso y generalmente con recursos propios. Se comunican a través de protocolos ligeros y/o estándares a través de servicios red. Al emplear protocolos sobre la red se tiene en contra el “overhead” resultado del paso de mensajes entre aplicaciones y los tiempos asociados a codificar y decodificar los mensajes enviados por emisor y receptor. Adicionalmente debemos asumir que la red no es fiable. Desarrollar la arquitectura microservicios lleva diseñar e implementar un sistema que tenga en cuenta las Falacias de la computación distribuida, en términos simples: a) la red tiene latencia, b) la red es insegura, c) el ancho de banda es limitado, d) existe un costo en el transporte de datos, e) la red puede cambiar, entre otros.

Sumarle a los elementos anteriores que muchas veces la comunicación interna de los microservicios se basa en http/rest/json en lugar de emplear (a lo interno) protocolos más ligeros que son más rápidos como gRPC. En un sistema monolítico estos temas son prácticamente irrelevantes.

La consistencia, cuando involucra varios microservicios siempre será eventual

La consistencia, tan necesaria en muchos sistemas a la vez que involucra operaciones de más de un microservicio siempre será eventual. Cuando se realiza una operación siempre existirá un dT (delta t, diferencial de tiempo) superior 0 entre que la información queda persistida en el microservicio A y el microservicio B. Unido a ello siempre, siempre será necesario tener en cuenta lo que se conoce como “acciones compensatorias”, una acción compensatoria es un operación que permite deshacer otra operación, similar al popular CTRL+Z presente en los entornos de escritorio, por ejemplo si tenemos que ejecutar una serie de acciones A → B → C → D y la acción D falla, entonces hay que ejecutar en orden inverso C → B →  A para poner el sistema en un estado consistente, en esta entrada hablamos del Patrón Saga, que resuelve este problema.

La consistencia no solo involucra la forma en que implementamos los microservicios; también influye, (y mucho) el mecanismo para la persistencia que empleemos. A partir de la conjetura de Brewer se formalizó el teorema CAP que nos sirve de ayuda para decidirnos a elegir un mecanismo de persistencia en base a las características deseadas de Consistencia, Disponibilidad y Soporte al particionado, las tres lamentablemente nunca están presentes en un sistema de persistencia.

Los microservicios surgen para soportar un alto throughput, que en muchas ocasiones deriva en almacenar datos. Almacenar datos que luego sean consultados de forma rápida y con tasas de tiempo cercanas a “tiempo real”; esto lleva consigo, en muchos casos buscar soluciones de persistencia que sacrifican la consistencia instantánea en favor de la tolerancia a particiones. Entonces, resumiendo hay dos factores que nos llevan a la consistencia eventual 1) la naturaleza distribuida de los microservicios y 2) el uso de sistemas de persistencias que pueden llegar a sacrificar la consistencia en dT=0 en favor de la escalabilidad horizontal.

Cada microservicio es una barrera de información

La migración de un monolito mediante la descomposición o el diseño de un nuevo sistema basado en microservicios lleva consigo la creación de backend(s) diferentes, con equipos diferentes y en ocasiones diversas tecnologías. Si bien tener equipos pequeños e independientes es una ventaja de la arquitectura microservicios, hay que tener claro que cada uno de los equipos necesita comunicarse con otros para temas de diseño, definir directrices para el paso de información, saber que microservicio va a exponer que funcionalidad en la plataforma, etc. Esta gobernanza en ocasiones se vuelve complicada cuando se segmenta demasiado la arquitectura. Patrones como PACT ayudan a esta tarea, pero siempre, hay que tener en cuenta que cada microservicio/equipo es una barrera y la gobernanza unida a un diseño bien claro son claves.

Implementar microservicios requiere de equipo maduros

El desarrollo de software requiere de profesionales preparados para crear soluciones mantenibles y escalables, conocimientos asociados al uso de patrones de diseño, arquitecturales, de comportamiento, funcionales y estructurales unido a prácticas de Clean Code es común encontrarlas en desarrolladores de software, sin embargo estas aunque son necesarias no son suficientes para personas que se enfrentan al desarrollo de una solución basada en microservicios, existen prácticas específicas que deben ser tenidas en cuenta a la hora de enfrentarse a desarrollar microservicios.

Un equipo de desarrollo que se va a enfrentar a una solución de microservicios debe conocer sobre patrones de descomposición que le servirán de base para el diseño de la arquitectura, este tipo de sistemas se diseña de forma completamente diferente a un sistema tradicional, por ejemplo si aplicamos el enfoque DDD hay particularidades en cuanto a la forma de comunicación entre los “bounded contexts”, el DDD clásico para el desarrollo de monolítos propone Open Host Service y Published Language, al extender esto a microservicios los contratos por interacción se definen (para casos api/rest) basado en Open API Specification. Así mismo existen otro grupo importante de prácticas que deben ser tenidas en cuenta como reintentos, circuit-breakers, configuración centralizada, registro y descubrimiento, api gateways, balanceo de carga, agregación de logs, traceo distribuido, etc. En realidad el desarrollo de microservicios lleva una carga cognitiva superior y requiere de equipos maduros y preparados.

Bueno, dejemos las críticas a un lado :-).

Espero les sirva la entrada para aprender más sobre los microservicios y los puntos que hay que tener claros a la hora de desarrollar sistemas basados en ello.