paint-brush
Automatización de diagramas de arquitectura de aplicaciones: cómo construí una herramienta para mapear bases de código desde la fuentepor@vladimirf
Nueva Historia

Automatización de diagramas de arquitectura de aplicaciones: cómo construí una herramienta para mapear bases de código desde la fuente

por Vladimir Filipchenko6m2024/07/30
Read on Terminal Reader

Demasiado Largo; Para Leer

¿Alguna vez has deseado que existiera una herramienta que pudiera convertir instantáneamente tu código en un diagrama visual claro? Bueno, ¡eso es exactamente lo que hace NoReDraw! Nacida de la frustración de un ingeniero de software, esta herramienta identifica componentes clave como artefactos y configuraciones, y los vincula para crear un diagrama de arquitectura integral. Está diseñado para ser súper personalizable y fácilmente ampliable, lo que garantiza que su documentación se mantenga actualizada sin la molestia de volver a dibujar diagramas cada vez que algo cambia.
featured image - Automatización de diagramas de arquitectura de aplicaciones: cómo construí una herramienta para mapear bases de código desde la fuente
Vladimir Filipchenko HackerNoon profile picture
0-item

Porque la vida es demasiado corta para volver a dibujar diagramas.


Recientemente me uní a una nueva empresa como ingeniero de software . Como siempre pasa, tuve que empezar de cero. Cosas como: ¿Dónde está activo el código de una aplicación? ¿Cómo se implementa? ¿De dónde vienen las configuraciones? Afortunadamente, mis colegas hicieron un trabajo fantástico al convertir todo en "infraestructura como código". Entonces me sorprendí pensando: si todo está en el código, ¿por qué no hay una herramienta para conectar todos los puntos?


Esta herramienta revisaría el código base y crearía un diagrama de arquitectura de la aplicación, destacando aspectos clave. Un nuevo ingeniero podría mirar el diagrama y decir: "Ah, está bien, así es como funciona".


Lo primero es lo primero

Por mucho que busqué, no pude encontrar nada parecido. Las coincidencias más cercanas que encontré fueron los servicios que dibujan un diagrama de infraestructura. Puse algunos de ellos en esta revisión para que puedas verlos más de cerca. Finalmente, dejé de buscar en Google y decidí intentar desarrollar algunas cosas nuevas e interesantes.


Primero, creé una aplicación Java de muestra con Gradle, Docker y Terraform. La canalización de acciones de GitHub implementa la aplicación en Amazon Elastic Container Service. Este repositorio será una fuente para la herramienta que crearé (el código está aquí ).


En segundo lugar, dibujé un diagrama de muy alto nivel de lo que quería ver como resultado:



Decidí que habría dos tipos de recursos:

Reliquia

El término artefacto me pareció demasiado sobrecargado, así que elegí Reliquia . Entonces, ¿qué es una reliquia? Es el 90% de todo lo que quieres ver. Incluyendo pero no limitado a:

  • Artefactos (cuadros azules en el esquema, es decir, tarros, imágenes de Docker),
  • Configura los recursos de Terraform (cuadros rosas en el esquema, es decir, instancias EC2, ECS, colas SQS),
  • recursos de Kubernetes,
  • y muchos muchos mas


Cada Reliquia tiene un nombre (p. ej., my-shiny-app), un tipo opcional (p. ej., Jar) y un conjunto de pares clave → valor (p. ej., ruta → /build/libs/my-shiny-app.jar) que describe completamente la reliquia. Se llaman Definiciones . Cuantas más definiciones tenga Relic, mejor.

Fuente

El segundo tipo es una Fuente . Las fuentes definen, construyen o aprovisionan Reliquias (p. ej., cuadros amarillos arriba). Una Fuente describe una Reliquia en algún lugar y da una idea de su origen. Si bien las Fuentes son los componentes de los que obtenemos la mayor cantidad de información, normalmente tienen significados secundarios en el diagrama. Probablemente no necesites muchas flechas desde Terraform o Gradle a cualquier otra Reliquia.


Reliquia y Fuente tienen una relación de muchos a muchos.


Divide y conquistaras

Cubrir cada fragmento de código es imposible. Las aplicaciones modernas pueden tener muchos marcos, herramientas o componentes de nube. ¡Solo AWS tiene alrededor de 950 recursos y fuentes de datos para Terraform! La herramienta tiene que ser fácilmente ampliable y desacoplada por diseño para que otras personas o empresas puedan contribuir.


Si bien soy un gran admirador de la arquitectura de los proveedores Terraform increíblemente conectables, decidí construir la misma, aunque simplificada:

Proveedores


El Proveedor tiene una responsabilidad clara: crear Reliquias basadas en los archivos fuente solicitados. Por ejemplo, GradleProvider lee archivos *.gradle y devuelve Jar , War o Gz Relics. Cada proveedor construye reliquias de los tipos que conoce. A los proveedores no les importan las interacciones entre Reliquias. Construyen Reliquias de forma declarativa, totalmente aisladas unas de otras.


Con ese enfoque, es fácil llegar tan profundo como quieras. Un buen ejemplo son las acciones de GitHub. Un archivo YAML de flujo de trabajo típico consta de docenas de pasos que utilizan componentes y servicios poco acoplados. Un flujo de trabajo podría crear un JAR, luego una imagen de Docker e implementarlo en el entorno. Cada paso del flujo de trabajo podría estar cubierto por su proveedor. Entonces, los desarrolladores de, digamos, Docker Actions crean un proveedor relacionado solo con los pasos que les interesan.


Este enfoque permite que cualquier número de personas trabajen en paralelo, añadiendo más lógica a la herramienta. Los usuarios finales también pueden implementar rápidamente sus proveedores (en el caso de alguna tecnología patentada). Vea más en Personalización a continuación.


Fusionar o no fusionar

Analicemos la siguiente trampa antes de pasar a la parte más jugosa. Dos proveedores, cada uno de los cuales crea una reliquia. Está bien. Pero ¿qué pasa si dos de estas Reliquias son sólo representaciones del mismo componente definido en dos lugares? Aquí hay un ejemplo.


AmazonECSProvider analiza la definición de tarea JSON y produce una Reliquia con el tipo AmazonECSTask . El flujo de trabajo de acciones de GitHub también tiene un paso relacionado con ECS, por lo que otro proveedor crea una reliquia de AmazonECSTaskDeployment . Ahora tenemos duplicados porque ambos proveedores no saben nada el uno del otro. Además, es incorrecto que cualquiera de ellos asuma que otro ya ha creado una Reliquia. ¿Y que?


Las reliquias se fusionan


No podemos descartar ninguno de los duplicados debido a las Definiciones (atributos) que tiene cada uno de ellos. La única manera es fusionarlos. De forma predeterminada, la siguiente lógica define la decisión de fusión:


 relic1.name() == relic2.name() && relic1.source() != relic2.source()


Fusionamos dos Reliquias si sus nombres son iguales, pero están definidas en Fuentes diferentes (como en nuestro ejemplo, JSON en el repositorio y la referencia de definición de tarea está en Acciones de GithHub).


Cuando nos fusionamos, nosotros:

  1. Elige un solo nombre
  2. Fusionar todas las definiciones (pares clave → valor)
  3. Crear una fuente compuesta que haga referencia a ambas fuentes originales.


Dibuja una línea

Omití intencionalmente un aspecto crucial de una reliquia. Puede que tenga un Matcher ... ¡y es mejor tenerlo! Matcher es una función booleana que toma un argumento y lo prueba. Los emparejadores son piezas cruciales de un proceso de vinculación. Si una Reliquia coincide con cualquier definición de la Reliquia de otra, estarán vinculadas entre sí.


¿Recuerdas cuando dije que los Proveedores no tienen idea de las Reliquias creadas por otros Proveedores? Eso sigue siendo cierto. Sin embargo, un Proveedor define un Comparador para una Reliquia. En otras palabras, representa un lado de una flecha entre dos cuadros en el diagrama resultante.


Coincidencia de reliquias


Ejemplo. Dockerfile tiene una instrucción ENTRYPOINT.


 ENTRYPOINT java -jar /app/arch-diagram-sample.jar


Con cierta certeza, podemos decir que Docker contiene todo lo que se especifica en ENTRYPOINT . Entonces, Dockerfile Relic tiene una función Matcher simple: entrypointInstruction.contains(anotherRelicsDefinition) . Lo más probable es que algunas Jar Relics con arch-diagram-sample.jar en las Definiciones coincidan. En caso afirmativo, aparece una flecha entre Dockerfile y Jar Relics.


Con Matcher definido, el proceso de vinculación parece bastante sencillo. El servicio de enlace itera sobre todas las Reliquias y llama a las funciones de su Matcher. ¿La Reliquia A coincide con alguna de las definiciones de la Reliquia B? ¿Sí? Agrega un borde entre esas Reliquias en el gráfico resultante. También se podría nombrar el borde.


Visualización

El último paso es visualizar nuestro gráfico final de la etapa anterior. Además del PNG obvio, la herramienta admite formatos adicionales, como Mermaid , Plant UML y DOT . Estos formatos de texto pueden parecer menos atractivos, pero la gran ventaja es que puedes incrustar esos textos en casi cualquier página wiki ( GitHub , confluencia , y muchos más).


Así es como se ve el diagrama final del repositorio de muestra:

diagrama final


Personalización

La capacidad de conectar componentes personalizados o modificar la lógica existente es esencial, especialmente cuando una herramienta se encuentra en su fase inicial. Las reliquias y las fuentes son lo suficientemente flexibles por defecto; puedes poner lo que quieras en ellos. Todos los demás componentes son personalizables. ¿Los proveedores existentes no cubren los recursos que necesita? Implemente el suyo propio con facilidad. ¿No está satisfecho con la lógica de fusión o vinculación descrita anteriormente? Ningún problema; agregue su propia LinkStrategy o MergeStrategy . Empaque todo en un archivo JAR y agréguelo al inicio. Leer más aquí .


Outro

La generación de un diagrama basado en el código fuente probablemente ganará terreno. Y la herramienta NoReDraw en particular (sí, este es el nombre de la herramienta de la que estaba hablando). ¡Los contribuyentes son bienvenidos !


El beneficio más notable (que proviene del nombre) es que no es necesario volver a dibujar un diagrama cuando cambian los componentes. La falta de atención de ingeniería es la razón por la cual la documentación en general (y los diagramas en particular) se vuelven obsoletos. Con herramientas como NoReDraw , ya no debería ser un problema, ya que se puede conectar fácilmente a cualquier canal de PR/CI. Recuerde, la vida es demasiado corta para volver a dibujar diagramas 😉