Compilación de microservicios en 2019 y en adelante

La arquitectura de microservicios se encuentra en constante evolución. Descubre prácticas recomendadas modernas para hacer las cosas bien.

Sten Pittet Sten Pittet

La de los microservicios es una popular y moderna práctica organizativa de ingeniería de software. El principio rector de los microservicios es compilar una aplicación dividiendo sus componentes empresariales en pequeños servicios que se puedan implementar y que funcionen de forma independiente los unos de los otros. La separación de intereses entre servicios se define como “límites de servicios”.

Los límites de servicios están estrechamente vinculados a las demandas empresariales y a los límites jerárquicos organizativos. Los servicios individuales pueden estar vinculados a los distintos equipos, presupuestos y hojas de ruta. Algunos ejemplos de límites de servicios pueden ser los servicios de procesamiento de pagos y autenticación de usuarios. Los microservicios difieren de las prácticas heredadas de desarrollo de softwar, en las que se agrupaban todos los componentes.

Este documento hace referencia a una empresa emergente imaginaria llamada “Pizzup” como representación de la aplicación de microservicios en una empresa moderna de software.

Cómo crear microservicios

Paso 1: comienza con un monolito

La primera práctica recomendada de microservicios es que probablemente no los necesites. Si no tienes ningún usuario para tu aplicación, es probable que los requisitos empresariales cambien rápidamente mientras estás desarrollando tu producto viable mínimo. Esto se debe sencillamente a la naturaleza del desarrollo de software y al ciclo de feedback que se debe producir durante la identificación de las capacidades empresariales clave que tu sistema debe proporcionar. Los microservicios añaden sobrecarga exponencial y complejidad de gestión. Por este motivo, en nuevos proyectos supone mucha menos carga de trabajo mantener todo el código y la lógica dentro de una sola base de código, ya que facilita el mover los límites de los diferentes módulos de tu aplicación.

Por ejemplo, con Pizzup podríamos haber empezado con una idea muy básica del problema que queremos resolver para nuestros clientes: queremos que la gente pueda pedir pizza por Internet.

Usuario de Pizzup diciendo: ¡Como usuario, puedo pedir pizza por Internet!

Cuando empecemos a pensar en los pedidos de pizza, comenzaremos a identificar las distintas capacidades necesarias en nuestra aplicación a fin de satisfacer esa necesidad. Deberemos saber gestionar una lista de las diferentes pizzas que podamos hacer, así como dejar a los clientes recoger una o varias pizzas, gestionar el pago, programar la entrega, etc. Asimismo, podemos decidir que la posibilidad de que los clientes creen una cuenta les permita repetir un pedido la próxima vez que usen Pizzup; además, después de hablar con nuestros primeros usuarios, podríamos descubrir que el seguimiento en directo de la entrega y la compatibilidad móvil definitivamente nos darían ventaja sobre la competencia.

Gráfico donde se muestra la diferencia entre los usos de usuario final y de administrador para la aplicación Pizzup.

Lo que al principio era una simple necesidad se convierte rápidamente en una lista de capacidades que debes proporcionar.

Los microservicios funcionan correctamente cuando conoces bien las funciones de los diferentes servicios que tu sistema necesita. Estos son mucho más difíciles de manejar si aún se están resolviendo los requisitos básicos de una aplicación. De hecho, resulta bastante costoso redefinir las interacciones de servicios, API y estructuras de datos en los microservicios ya que puedes tener muchos más elementos que se deben coordinar. Por esta razón, aconsejamos simplificar las cosas hasta que hayas recopilado suficientes comentarios de los usuarios para tener la seguridad de que se han comprendido las necesidades básicas de tus clientes y que se ha llevado la planificación de acuerdo con ellas.

Sin embargo, una pequeña advertencia: la creación de un monolito puede dificultar rápidamente el desglose de un código en fragmentos más pequeños. En la medida de lo posible, intenta tener identificados módulos claros para poder extraerlos más tarde del monolito. Asimismo, puedes empezar separando la lógica de tu interfaz de usuario web y asegurarte de que interactúa con tu backend mediante una API de RESTful a través de HTTP. Esto facilitará la transición a microservicios en el futuro cuando empieces a mover algunos de los recursos de la API a distintos servicios.

Paso 2: organiza tu equipo de forma correcta

Hasta ahora, parecía que la compilación de microservicios era, sobre todo, una cuestión técnica. Deberás dividir una base de código en varios servicios, implementar los patrones correctos para fallar de forma limpia y recuperarte de las incidencias en la red, lidiar con la coherencia de los datos, supervisar la carga de servicio, etc. Habrá muchísimos conceptos nuevos que comprender, pero una cosa que no se debe ignorar es que deberás reestructurar la forma en que se organizan tus equipos.

Cualquier organización que diseñe un sistema (ampliamente definido) producirá un diseño cuya estructura es una copia de la estructura de comunicación de la organización.

- Conway's Law

La ley de Conway existe y se puede observar en cualquier tipo de equipo, y si un equipo de software está organizado con un equipo de backend, uno de frontend y otro de operaciones, terminarán entregando monolitos de frontend y backend separados que se presentarán al equipo de operaciones para que los gestionen en la producción.

Este tipo de estructura no es una buena opción para los microservicios, ya que cada servicio puede verse como su propio producto que debe enviarse independientemente de los demás. En su lugar, debes crear equipos más pequeños que tengan todas las competencias necesarias para desarrollar y mantener los servicios de los que están a cargo. Werner Vogels, director de tecnología de Amazon, describió esta situación con la frase “tú lo creas y tú lo ejecutas”. Organizar tus equipos de esta manera presenta grandes ventajas. En primer lugar, todos tus desarrolladores comprenderán mejor el impacto de tu código en la producción; esto ayudará a producir una mejor publicación y a reducir el riesgo de ver incidencias publicadas en tus clientes. En segundo lugar, tus implementaciones resultarán más naturales para cada equipo, ya que podrán trabajar juntos en las mejoras en el código, así como en la automatización de la canalización de implementaciones.

Paso 3: divide el monolito para compilar una arquitectura de microservicios

Cuando hayas identificado los límites de tus servicios y hayas descubierto cómo puedes cambiar tus equipos para que sean más verticales en lo que respecta a competencias, puedes empezar a dividir el monolito para crear microservicios. Estos son los puntos clave que tener en cuenta en ese momento.

Simplifica la comunicación entre servicios con una API basada en REST

Si aún no estás usando una API basada en REST, ahora sería un buen momento para adoptarla en tu sistema. Como explica Martin Fowler, te interesará tener “endpoints inteligentes y canales simples”. Esto quiere decir que el protocolo de comunicación entre tu servicio debe ser lo más sencillo posible y que solo se encargue de transmitir los datos sin transformarlos. La magia se producirá en los mismos endpoints: reciben una solicitud, la procesan y emiten una respuesta a cambio.

Es también aquí cuando los microservicios se pueden distinguir de SOA evitando la complejidad del bus de servicio empresarial. Las arquitecturas de microservicios se esfuerzan por mantener las cosas lo más sencillas posible para evitar dependencias estrechas entre componentes. En algunos casos, es posible que acabes utilizando una arquitectura basada en eventos con comunicaciones asincrónicas basadas en mensajes. Pero, una vez más, debes revisar los servicios básicos de cola de mensajes como RabbitMQ y evitar añadir complejidad a los mensajes transmitidos a través de la red.

Divide la estructura de tus datos

Es bastante común tener una sola base de datos para las diferentes capacidades de un monolito. Cuando un usuario accede a su pedido, buscarás directamente en la tabla del usuario para ver la información del cliente, y la misma tabla puede servir para rellenar la factura gestionada por el sistema de facturación. Esto parece lógico y sencillo pero, con los microservicios, querrás que los servicios se desacoplen para que sea posible acceder a las facturas aunque el sistema de pedidos se haya caído, y porque permite optimizar o perfeccionar la tabla de facturas independientemente de las demás. Esto significa que cada servicio puede terminar teniendo su propio almacén de datos para mantener los datos que necesita.

Obviamente, también presenta nuevos problemas, ya que acabarás con algunos datos duplicados en diferentes bases de datos. En este caso, debes ponerte como objetivo la futura coherencia y puedes adoptar una arquitectura basada en eventos para poder sincronizar los datos a través de varios servicios. Por ejemplo, es posible que tus servicios de facturación y seguimiento de entregas estén escuchando eventos emitidos por los servicios de cuentas cuando un cliente actualiza su información personal. Tras recibir el evento, esos servicios actualizarán en su almacén de datos de forma pertinente. Esta arquitectura basada en eventos permite facilitar la lógica del servicio de la cuenta, ya que no tiene que conocer los demás servicios dependientes. Simplemente le dice al sistema lo que ha hecho y otros servicios escuchan y actúan en consecuencia.

También puedes conservar toda la información del cliente en el servicio de la cuenta y mantener únicamente una referencia de clave externa en tu servicio de facturación y entrega. Luego, interactuarían con el servicio de la cuenta para obtener los datos pertinentes del cliente cuando fuera necesario, en lugar de duplicar los registros existentes. No hay soluciones universales para estos problemas y tendrás que analizar cada caso específico para determinar cuál es el mejor enfoque.

Compila tu arquitectura de microservicios en caso de fallos

Ya hemos visto las grandes ventajas que pueden ofrecerte los microservicios sobre una arquitectura monolítica. Son de tamaño más pequeño y están especializados, de modo que son fáciles de entender. Están desacoplados, lo que significa que puedes refactorizar un servicio sin temer estropear los demás componentes del sistema, o bien ralentizar el desarrollo de los otros equipos. Asimismo, ofrecen más flexibilidad a tus desarrolladores, ya que pueden elegir diferentes tecnologías si es necesario sin verse limitados por las necesidades de los otros servicios.

En resumen, contar con una arquitectura de microservicios facilita el desarrollo y el mantenimiento de cada capacidad empresarial. Pero las cosas se vuelven más complicadas cuando nos fijamos en todos los servicios en conjunto y en cómo deben interactuar para realizar acciones. Tu sistema ahora está distribuido con varios puntos de error y debes hacerte cargo de eso. Debes tener en cuenta no solo los casos en los que un servicio no responde, sino también la capacidad de lidiar con respuestas de red más lentas. A veces, recuperarse de un error también puede resultar complicado, ya que debes asegurarte de que los servicios que vuelvan a ponerse en marcha no se inunden de mensajes pendientes.

Cuando empieces a extraer capacidades de tus sistemas monolíticos, asegúrate de que tus diseños están preparados para evitar los errores desde el principio.

Haz hincapié en la supervisión para facilitar las pruebas de microservicios

Las pruebas son otro inconveniente de los microservicios en comparación con un sistema monolítico. Una aplicación compilada como una sola base de código no necesita mucho para tener un entorno de pruebas en marcha. En la mayoría de los casos, tendrás que iniciar un servidor de backend acoplado con una base de datos para poder ejecutar tu conjunto de pruebas.

En el mundo de los microservicios, las cosas no son tan fáciles. Cuando se trata de pruebas unitarias, seguirá siendo bastante similar al enfoque monolítico y no deberías tener más dificultades a ese nivel. Sin embargo, en lo que respecta a la integración y las pruebas del sistema, las cosas se ponen mucho más difíciles. Es posible que tengas que iniciar varios servicios a la vez y poner en marcha diferentes almacenes de datos, y que tu configuración deba incluir colas de mensajes que no necesitabas con el monolito. En este caso, resulta mucho más costoso realizar pruebas funcionales y el cada vez mayor número de elementos móviles complica mucho la predicción de los diferentes tipos de errores que se pueden producir.

Es por ello por lo que debes hacer especial hincapié en la supervisión para poder identificar pronto las incidencias y reaccionar en consecuencia. Tendrás que entender los puntos de referencia de tus diferentes servicios y saber reaccionar no solo cuando dejen de estar operativos, sino también cuando se comporten de manera inesperada. Una ventaja de adoptar una arquitectura de microservicios es que tu sistema debe ser resistente a errores parciales, por lo que, si empiezas a ver anomalías en el servicio de seguimiento de entregas de nuestra aplicación Pizzup, no será tan malo como si se tratara de un sistema monolítico. Nuestra aplicación debe diseñarse para que todos los demás servicios respondan correctamente y dejar que nuestros clientes pidan pizza mientras restauramos el seguimiento en vivo.

Adopta la entrega continua para reducir la fricción de las implementaciones

La publicación manual de un sistema monolítico en la producción es una tarea tediosa y arriesgada, pero no imposible. Por supuesto, no recomendamos este enfoque y animamos a todos los equipos de software a adoptar la entrega continua para todo tipo de desarrollo; sin embargo, al comienzo de un proyecto, podrías encargarte tú mismo de las primeras implementaciones a través de la línea de comando.

Este enfoque no resulta sostenible cuando se tiene un número cada vez mayor de servicios que se deben implementar varias veces al día. Por lo tanto, como parte de la transición a los microservicios, es muy importante que adoptes la entrega continua para reducir el riesgo de fallos de publicación, así como asegurarte de que tu equipo se centra en la compilación y ejecución de la aplicación, en lugar de quedarse atascado en la implementación. Poner en práctica la entrega continua también significará que tu servicio ha aprobado las pruebas de aceptación antes de la producción. Por supuesto, se producirán errores pero, con el tiempo, crearás un sólido conjunto de pruebas que aumentará la seguridad de tu equipo con respecto a la calidad de las publicaciones.

La ejecución de microservicios no es un sprint

Los microservicios se están convirtiendo rápidamente en una práctica recomendada muy adoptada en el sector. En el caso de proyectos complejos, ofrecen una mayor flexibilidad en la forma de desarrollar e implementar software. Asimismo, ayudan a identificar y formalizar los componentes empresariales de tu sistema, lo que resulta útil cuando se tienen varios equipos trabajando en la misma aplicación. Pero también existen inconvenientes claros en la gestión de sistemas distribuidos, y la división de una arquitectura monolítica solo se debe realizar cuando se comprenden claramente los límites del servicio.

La compilación de microservicios debe considerarse un viaje, no el objetivo inmediato de un equipo. Empieza poco a poco para conocer los requisitos técnicos de un sistema distribuido, cómo fallar de forma limpia y cómo escalar componentes individuales. Luego, puedes extraer gradualmente más y más servicios a medida que vas adquiriendo experiencia y conocimientos.

La migración a una arquitectura de microservicios no tiene que llevarse a cabo mediante un único esfuerzo holístico. Una apuesta más segura es la estrategia iterativa de migrar secuencialmente componentes más pequeños a los microservicios. Identifica los límites de servicio mejor definidos dentro de una aplicación monolítica establecida y trabaja iterativamente para desacoplarlos en su propio microservicio.

Resumen

En resumen, los microservicios constituyen una estrategia beneficiosa tanto para el proceso de desarrollo de código técnico sin procesar como para la estrategia general de la organización empresarial. Además, ayudan a organizar los equipos en unidades centradas en desarrollar y poseer funciones empresariales específicas. Este enfoque granular mejora la comunicación y la eficiencia empresariales a nivel general. Hay compensaciones para los beneficios de los microservicios. Es importante que los límites de servicio estén claramente definidos antes de migrar a una arquitectura de microservicios. La arquitectura de microservicios aún es bastante reciente, pero es una forma prometedora de desarrollar aplicaciones y definitivamente vale la pena estudiarla. No olvides que es posible que aún no encaje del todo en tu equipo.

A continuación
What are containers?