Compartir vía


Diseño para evolucionar

Un diseño evolutivo es clave para una innovación continua.

Todas las aplicaciones que tienen éxito cambian a lo largo del tiempo para corregir errores, agregar nuevas características, incorporar nuevas tecnologías o hacer que los sistemas existentes sean más resistentes y escalables. Si todas las partes de una aplicación están estrechamente acopladas, resulta muy difícil introducir cambios en el sistema. Un cambio en una parte de la aplicación podría interrumpir otra parte o provocar cambios que se propaguen por todo el código base.

Este problema no se limita a las aplicaciones monolíticas. Una aplicación puede descomponerse en servicios, pero aún así mostrar el tipo de acoplamiento estrecho que deja el sistema rígido y frágil. Pero cuando los servicios están diseñados para evolucionar, los equipos pueden innovar y proporcionar continuamente nuevas características.

Los microservicios se están convirtiendo en una manera popular de lograr un diseño evolutivo, ya que abordan muchos de los aspectos que se enumeran aquí.

Recomendaciones

Exigir una cohesión alta y un acoplamiento flexible. Un servicio está cohesionado si proporciona una funcionalidad que está unida de forma lógica. Unos servicios tienen un acoplamiento flexible si puede cambiar un servicio sin cambiar el otro. La alta cohesión generalmente significa que los cambios en una función requerirán cambios en otras funciones relacionadas, cuando todas las funciones relacionadas residen en un servicio. Si encuentra que la actualización de un servicio requiere actualizaciones coordinadas para otros servicios, puede ser un signo de que los servicios no sean coherentes. Uno de los objetivos del diseño basado en el dominio (DDD) es identificar estos límites.

Encapsular el conocimiento del dominio. Cuando un cliente utiliza un servicio, la responsabilidad de aplicar las reglas de negocio del dominio no debe recaer en el cliente. En su lugar, el servicio debe encapsular todo el conocimiento del dominio que recae bajo su responsabilidad. De lo contrario, todos los clientes tienen que aplicar las reglas de negocio y termina con el conocimiento de dominio distribuido en diferentes partes de la aplicación.

Utilizar la mensajería asincrónica. La mensajería asincrónica es una manera de desacoplar el productor de mensajes del consumidor. El productor no depende del consumidor que responda al mensaje ni de realizar ninguna acción concreta. Con una arquitectura pub/sub, es posible que el productor ni siquiera sepa quién consume el mensaje. Los nuevos servicios pueden consumir fácilmente los mensajes sin modificaciones en el productor.

No crear conocimientos de dominio en una puerta de enlace. Las puertas de enlace pueden ser útiles en una arquitectura de microservicios para cosas como enrutamiento de solicitud, traducción de protocolos, equilibrio de carga o autenticación. Sin embargo, la puerta de enlace debe estar restringida a este tipo de funcionalidad de infraestructura. No debe implementar ningún conocimiento del dominio para evitar convertirse en una dependencia intensa.

Exponer interfaces abiertas. Evite crear capas de traducción personalizadas que residan entre los servicios. En su lugar, un servicio debe exponer una API con un contrato de API bien definido. La API debe estar versionada para que pueda evolucionar mientras mantiene la compatibilidad con versiones anteriores. De este modo, puede actualizar un servicio sin coordinar las actualizaciones de todos los servicios ascendentes que dependen de él. Los servicios orientados al público deben exponer una API de RESTful a través de HTTP. Los servicios back-end pueden usar un protocolo de mensajería de estilo RPC por motivos de rendimiento.

Diseño y prueba con contratos de servicio. Cuando los servicios exponen unas API bien definidas, puede desarrollar y probar mediante estas API. De este modo, puede desarrollar y probar un servicio individual sin poner en marcha todos sus servicios dependientes. (Por supuesto, seguiría realizando pruebas de integración y carga en los servicios reales).

Utiliza funciones de fitness. Las funciones de fitness miden el resultado para ver si está más cerca o más lejos de una solución óptima. Las funciones de fitness ayudan a proteger las características de arquitectura a medida que se producen cambios con el tiempo. La función fitness es cualquier mecanismo que proporciona una evaluación objetiva de la integridad de las características de la arquitectura. La evaluación puede incluir varios mecanismos, como métricas, pruebas unitarias e ingeniería de caos. Por ejemplo, el arquitecto podría identificar el tiempo de carga de página como una característica importante. Posteriormente, la carga de trabajo debe tener una función de fitness para probar el tiempo de carga de página y ejecutar la prueba como parte de la integración continua.

Infraestructura abstracta fuera de la lógica del dominio. No permita que la lógica del dominio se mezcle con una funcionalidad relacionada con la infraestructura, como la mensajería o la persistencia. En caso contrario, los cambios en la lógica del dominio requerirán actualizaciones en las capas de infraestructura y viceversa.

Delegar preocupaciones transversales a un servicio independiente. Por ejemplo, si varios servicios necesitan autenticar solicitudes, puede mover esta funcionalidad a su propio servicio. Puede desarrollar luego el servicio de autenticación, por ejemplo, agregando un nuevo flujo de autenticación, sin tocar ninguno de los servicios que lo usan.

Implementar servicios de forma independiente. Cuando el equipo de DevOps puede implementar un único servicio independientemente de otros servicios de la aplicación, las actualizaciones pueden realizarse de forma más rápida y segura. Las correcciones de errores y las nuevas características pueden implementarse a un ritmo más regular. Diseñe la aplicación y el proceso de lanzamiento para que admitan actualizaciones independientes.