jueves, 27 de octubre de 2022

Desarrollo ágil con Arquitectura Hexa3

 ¿Cómo podemos usar arquitectura limpia para desarrollar ecosistemas de aplicaciones distribuidas con equipos ágiles?

Patrón de tres capas

Antes de contestar esta pregunta anterior, podemos comenzar por… ¿Cómo podemos implementar una arquitectura limpia?

  • Domain: esta capa se encarga del “¿Cómo hacen los casos de uso para cumplir su función?”, es decir: el código de lógica, lógica compartida, de negocio o de dominio. Esto incluye los servicios de dominio y modelo de datos (agregados, entidades, value object, etc.).
  • Infrastructure: aquí va el código de acceso a recursos (acceso a base de datos, recursos, archivos, clientes api para servicios externos, consumo de colas de eventos específicos, etc.).
Ejemplo de 3 capas basado en DDD
Ejemplo de una estructura de backend MERN NestJS en 3 capas

Patrón de puertos y adaptadores

Claro que con separar en capas no alcanza para tener un código limpio y lejos de ser una madeja de código en monolito. Necesitamos seguir un gran principio en el desarrollo de software: “aumentar cohesión y disminuir acoplamiento”; y eso lo podemos hacer aplicando el principio de inyección de dependencias (“Dependency inversion principle”) con el patrón de “puertos y adaptadores”, también conocida como arquitectura de “Ports and Adapters” o como parte de la Arquitectura Hexagonal.

  • Adaptador: Es la implementación de la interfaz, en ella se generará el código específico para consumir una tecnología en concreto. Esta nunca se usará de forma directa desde otras capas, ya que su uso se realizará a través del tipo del puerto.
Ejemplo de puerto/adaptador en tres capas
Ejemplo con el patrón repositorio.
Ejemplo de inyección de dependencia con anotaciones NestJS (MERN stack) donde se inyecta en el dominio la implementación de infra. Se usa el patrón repositorio (código del ejemplo anterior).

La pauta del dominio como núcleo

Además, si nos guiamos por la propuesta de la Arquitectura Hexagonal que consiste en que el “dominio sea el núcleo de las capas y que este no se acople a nada externo”. Entonces será nuestro dominio el que contenga los puertos de entrada (incoming) y salida (outgoing). Es decir que toda acción o evento externo (i.e. como pedidos http) que llega a la aplicación por un puerto, es convertido, a través de un adaptador específico, a la tecnología del evento exterior, pasando por el dominio solo a través de sus puertos. Por ejemplo la GUI es un ejemplo de un adaptador que transforma las acciones de un usuario en la API de su puerto. La inversión de dependencia hace que la capa de aplicación y la capa de infraestructura conozcan a la capa de dominio, pero no a la inversa. Y en el caso de la capa de aplicación y la de infraestructura, es la de aplicación quién puede conocer a la de infraestructura y no a la inversa.

Diagrama de dominio como núcleo

Aplicación hexagonal

Una manera de interpretar e implementar esta arquitectura es considerar cada gran pieza de software (que será un proyecto), como una aplicación que empaqueta un conjunto de funcionalidades bajo algún concepto o dominio de problema.

Ejemplo de un API backend hexagonal

Frontend hexagonal

Inclusive, en aplicaciones cliente servidoras u orientadas a micro-servicios, un frontend puede ser desarrollado como una app hexagonal en sí misma, que depende de otras que forman el backend. Habitualmente no se implementa la arquitectura hexagonal de esta manera y se llega a tener frontends que son una verdadera madeja enredada de código. La propuesta aquí es aplicar arquitectura hexagonal también al frontend.

Ejemplo de una aplicación cliente-servidor
Ejemplo de estructura de un proyecto frontend en React (MERN stack)

Testing por capas

También tenemos que pensar en el testing. Sin ‘test’ no hay software de calidad. Un beneficio importante de esta arquitectura de software es que facilita la automatización de pruebas independientes por capas usando inyección de dependencias de Mocks y/o Stubs.

Ejemplo de full test por capas

Estructora de carpetas

Al implementar la arquitectura en un proyecto recomiendo que quede la distinción explícita en 3 directorios (uno por capa). Un ejemplo es como la siguiente:

Estructura de carpetas en un proyecto Back-end

Red de aplicaciones hexagonales

Entonces… ¿Cómo podemos desarrollar ecosistemas de aplicaciones distribuidas con arquitectura limpia? Lo podemos hacer con esta arquitectura hexagonal, distribuida, orientada a microservicios y microapps. Es decir que, en sistemas mayores o en el escalamiento, se pueden conformar sistemas distribuidos como una red de aplicaciones hexagonales interconectadas. Es decir que podemos desarrollar ecosistemas de aplicaciones interdependientes, aunque robustas, y conformadas por micro-apps (incluyendo micro-frontends de ser necesario) y micro-services hexagonales.

Red de equipos ágiles

En honor al principio de Conway, que dice que las estructuras del sistema desarrollado serán congruentes con las estructuras sociales de la organización que lo produce, es que tendremos un ecosistema de equipos acorde a esta arquitectura. En este sentido, tendríamos equipos encargados cada uno de una o varias Apps relacionadas bajo un mismo dominio de contexto. Estos equipos deberían poder desplegar software de forma independiente, en sus propios repositorios y entregar de forma evolutiva e incremental.

Vertical slicing

Y para entregar de forma evolutiva e incremental, los equipos pueden desarrollar desglosando entregables/soluciones en features con ‘vertical slicing’ que a su vez se pueden desglosar en historias de usuario. En un vertical slice pueden haber una o más historias de usuario y/o historias técnicas para completar una feature completa.

Ejemplo de ‘vertical slicing’ de una feature

Desarrollo evolutivo

Estos entregables/soluciones evolucionarían desde algún MVP a productos más complejos, adaptándose a las necesidades de los usuarios según la población foco de cada momento cronológico del ciclo de vida del producto/servicio.

Conclusión

Finalmente, concluyo que estos patrones de arquitectura, como otros semejantes, ofrecen una manera de aplicar buenas practicas de código para crear código funcional, mantenible, de fácil crecimiento y que satisfaga las necesidades de los usuarios. Lo que se recomienda es usar los principios de desarrollo de software que llevan a este tipo de arquitecturas, en busca de encontrar mejores formas de desarrollar software de calidad.

  • Principio de ‘Dependency Inversion’ con el patrón ‘Ports and Adapters’ y el patrón de diseño ‘Dependency Injection’.
  • El principio de ‘Testing’ con el patrón de diseño ‘Dependency Injection’ (de Mocks y Stubs).
  • Y por último, el clásico principio de “más cohesión y menos acoplamiento”.
  1. Applying UML and Patterns,1997, Craig Larman.
  2. Presentation Domain Data Layering, 2015, Martin Fowler.
  3. Artículo de softwarecrafters: Arquitectura hexagonal frontend.
  4. Article: Domain Driven Design, Project structure.
  5. Article: What is the 3-Tier Architecture? 2012 by Tony Marston.
  6. Artículo en Medium: Arquitectura Hexagonal.
  7. Article: Hexagonal Architecture applied to typescript react project.
  8. Article: “Hexagonal Architecture: three principles and an implementation example”.
  9. Arquitectura Hexagonal con Typescript en APIs web con Nodejs.
  10. Ejemplo de proyecto backend hexagonal/DDD con NestJs: https://github.com/ecaminero/nestjs-ddd
  11. https://javascript.plainenglish.io/applying-atom-design-methodology-and-hexagonal-architecture-using-react-6dbb1863a5d5
  12. https://medium.com/ssense-tech/hexagonal-architecture-there-are-always-two-sides-to-every-story-bc0780ed7d9c
  13. Ejemplo code de clean architecture: https://github.com/bazaglia/shopping-cart


viernes, 24 de junio de 2022

Paper-Book: resumen de el nuevo nuevo juego de desarrollo de productos que dio origen a Scrum

Si quieres conocer realmente el corazón del marco de trabajo Scrum, debes leer el paper de Hirotaka Takeuchi and Ikujiro Nonaka que le dio origen a principios de los 80. Aunque, si no lo has leído, aquí te comparto mi resumen.

En su estudio y paper “The New New Product Development Game”, Nonaka y Takeuchi compararon una nueva forma de trabajo en equipo, con el avance en formación de scrum de los jugadores de Rugby, a raíz de lo cual quedó acuñado el término 'Scrum' para referirse a ella.


Estos autores ofrecieron el enfoque de Scrum como alternativa al enfoque tradicional secuencial (cascada) o al de "carrera de relevos", para el desarrollo de productos en el vertiginoso mundo actual, con contextos de requisitos inestables, donde se necesita velocidad y flexibilidad. Ellos propusieron un enfoque holístico, compuesto por un núcleo de seis características: 
  1. Control por misión (Built-in Instability): la alta dirección crea un elemento de tensión en el equipo al darle gran libertad para llevar a cabo un proyecto de importancia estratégica para la empresa y al mismo tiempo establecer requisitos muy desafiantes. Es decir: dar gran libertad y objetivos muy exigentes. Dar libertad con exigencia. Algo que los autores llamaron 'Built-in Instability' y que prefiero, por simplicidad, llamar "control por misión".

  2. Equipos autoorganizados: es un equipo que exhibe tres condiciones: autonomía, autotrascendencia y fertilización cruzada. En el día a día, la alta dirección rara vez interviene; el equipo es libre de establecer su propia dirección. En cierto modo, la alta dirección actúa como capitalista de riesgo. O como dijo un ejecutivo: “Abrimos nuestro bolso pero mantenemos la boca cerrada”.

  3. Fases de desarrollo superpuestas: es la integración de diferentes profesionales de una cadena de valor para lograr objetivos comunes, en vez del trabajo aislado y en secuencia. Aquí se introduce la idea del rugby, las fases se superponen considerablemente, lo que permite que el grupo absorba la vibración o “ruido” generado a lo largo del proceso de desarrollo trabajando como bloque unido.  Un equipo trata de recorrer la distancia como una unidad, pasándose el balón de un lado a otro, en equipo.

  4. Control sutil: se busca seleccionar a las personas adecuadas, crear un entorno de trabajo abierto, alentar a los ingenieros a salir al campo y escuchar lo que los clientes y stakeholders tienen que decir, establecer un sistema de evaluación y recompensa basado en el desempeño del equipo, manejar las diferencias de ritmo a lo largo del proceso de desarrollo y tolerar y anticipar los errores. La gerencia establece suficientes puntos de control para evitar que la inestabilidad, la ambigüedad y la tensión se conviertan en caos; y al mismo tiempo, la gerencia evita el tipo de control rígido que perjudica la creatividad y la espontaneidad fomentando el ‘autocontrol’. 

  5. Aprendizaje múltiple (Multilearning): hay que dedicar tiempo al aprendizaje individual constante, fomentando la iniciativa y el aprendizaje práctico por parte de los empleados y ayudando a mantenerlos al día con los últimos avances. Además incluye el aprendizaje en otras áreas de la experticia de cada profesional.

  6. Aprendizaje Organizacional: los autores proponen la 'transferencia organizacional del aprendizaje' que incluye el impulso para acumular conocimiento a través de niveles y funciones distintas en el ecosistema de la organización; y el impulso por parte de los miembros del equipo para transferir su aprendizaje a otros fuera del grupo. El conocimiento también se transmite en la organización al convertir las actividades del proyecto en una práctica estándar. Es decir que se busca en estos dos últimos puntos el aprendizaje multi-nivel, multi-funcional y transversal en la organización, algo que podemos llamar aprendizaje organizacional.


Si buscamos seguir estas claves en el desarrollo de software o en nuestra organización, nos estaremos acercando a este Scrum pragmático. El framework, en el fondo termina implementando estas ideas, aunque fue años más tarde cuando fue creado por Ken Schwaber y Jeff Sutherland, quienes le dan forma y lo popularizan, basados en la idea de estos autores, en sus propias experiencias y en los numerosos experimentos del incipiente framework hechos por Jeff. Pero esa es otra historia.




Nota: la traducción de las seis características no son textuales, sino que se ajustaron según el autor de este artículo tomando una licencia por didactismo, ya que algunas frases del Inglés generan algo de confusión en su interpretación.