PlantUML y C4

Desde hace muchos años, el mundo del desarrollo del software cuenta con lenguajes de modelado de sistemas, tipo UML, ArchiMate o SysML, usados para definir la arquitectura mediante diagramas que ayuden a la comprensión del mismo. Sin embargo en la práctica (al menos la que yo he vivido) poca gente conoce y mucha menos usa algunos de estos lenguajes. Probablemente son tan completos que al final resultan demasiado complejos y con demasiados detalles por lo que se termina optando por diagramas con solo cajas y líneas sin mucho sentido e incongruentes entre sí.

C4

El modelo C4 pretende simplificar la forma de explicar la arquitectura software mediante una "aproximación" al detalle dividida en 4 pasos, o zooms:

  • Contexto

  • Contenedores (nada que ver con Docker)

  • Componentes

  • Código

Podemos ver el primer nivel, Contexto, como el diagrama que representa al más alto nivel la solución que tratamos de explicar. A su vez, el siguiente nivel de Contenedores (repetimos, nada que ver con contenedores Docker) muestra bloques software de alto nivel mientras que el de Componentes explica contenedor a contenedor qué partes componen cada uno. Por último quien busca el detalle más exhaustivo acude al diagrama de Código donde podemos representar las clases, interfaces, etc y sus relaciones internas.

c4 overview

C4 busca la simplicidad así que trabaja simplemente con:

  • Person, el típico actor, role, persona, etc en definitiva un humano usando el software

  • Contenedor, entendido como una aplicación o una base de datos. Algo que tiene que se tiene que ejecutar, por ejemplo: un war corriendo en un Tomcat o un Node, una aplicación que se ejecuta en el desktop del cliente, una base de datos o un simple script.

  • Componente, en este contexto se entiende como un grupo de funcionalidades relacionadas con un interface común. Lo normal es que un conjunto de componentes que comparten un contenedor se ejecutan en el mismo espacio de trabajo

En este post vamos a ver cómo podemos aplicar este modelo en nuestra documentación usando PlantUML (y/o Asciidoctor).

PlantUML

PlantUML es una herramienta para generar diagramas de software (y otros) partiendo de texto. La idea es muy potente porque te permite tener tus diagramas versionados como si fueran parte del código pudiendo versionarlos en un repositorio, fomentar la revisión, etc. Unido a herramientas como Asciidoctor, donde tu documentación sigue el mismo principio de ser texto y que la herramienta genere el resultado visual, disponemos de una forma cómoda y potente de tener nuestra documentación al día y visualmente atractiva.

PlantUML (https://www.plantuml.com) te permite generar diagramas:

  • de clases

  • de actividad

  • secuencia

  • componentes

  • etc

Así mismo cuenta con un sistema para poder extender sus capacidades pudiendo incluir iconos, otros tipos de diagramas, etc que es lo que vamos a usar en este post para demostrar cómo documentar una arquitectura software con C4.

Básicamente la idea es que puedas adjuntar junto a tus explicaciones en Asciidoctor, bloques de texto como los que se muestran a continuación, de tal forma que no necesites herramientas externas ni incluir imágenes que no puedas volver a editar cuando quieras añadir o quitar información.

Contexto

El primer diagrama que generaremos es un diagrama de Contexto donde mostraremos la foto general sin mucho detalle. Mostraremos las Persons principales así como sistemas externos. En este diagrama no buscamos mostrar tecnología, sino relaciones entre las personas y los sistemas

context.adoc
[plantuml]
----
!include <c4/C4_Context.puml>
!include <office/Users/user.puml>
!include <office/Users/mobile_user.puml>

title Te lo traigo de mi Pueblo

LAYOUT_TOP_DOWN
LAYOUT_WITH_LEGEND()

Person(customer, "<$user>\nCliente", "Un cliente de TelotraigodemiPueblo.")

Enterprise_Boundary(c0, "Te lo traigo de mi Pueblo") {
    Person(csa, "<$mobile_user>\nCustomer Service Agent", "Help Desk.")

    System(ecommerce, "E-commerce System", "Registro, planificacion y suscripcion.")
}

System_Ext(banco, "PasarelaPago", "Pagos de pedidos.")

Rel_D(customer, csa, "Soporte", "Telefono")

Rel_D(customer, ecommerce, "Crea viajes y realiza pedidos")

Rel_D(ecommerce, banco, "Procesa pago")
----
context
Figure 1. Context

En el diagrama de contexto simplemente reflejamos Personas (Cliente y Help Desk) e indicamos qué partes pertenecen al dominio a representar y cuales son externas ( System vs System_Ext )

La extensión nos permite de forma muy simple indicar cómo queremos ubicar los elementos entre sí: Rel(a,b) una relación entre a y b normal, Rel_D(a,b) una relación top-down mientras que Rel_U(a,b) hace que la relación sea down-top. Podemos usar Rel_L y Rel_R para izquierda y derecha

Container

Como hemos dicho, un container es un backend, una página web, una aplicación mobile, en resumen una unidad "ejecutable"

En nuestro ejemplo vamos a disponer de 3 containers: un Single Page Application como front que a través de llamadas http comunicará con otro container Backend, el cual usa una base de datos para guardar la información. ( En nuestro ejemplo por simplicidad, el backend se comunicará con la pasarela de pago directamente aunque podríamos crear otros containers especializados)

Container.adoc
[plantuml]
----
!include <c4/C4_Container.puml>
!include <office/Users/user.puml>
!include <office/Users/mobile_user.puml>
!include https://raw.githubusercontent.com/jagedn/mn-plantuml-sprites/master/sprites/grails.puml

title Te lo traigo de mi Pueblo

LAYOUT_TOP_DOWN
LAYOUT_WITH_LEGEND()

Person_Ext(anonymous_user, "Anonymous User")
Person(aggregated_user, "Aggregated User")
Person(administration_user, "Administration User")

System_Boundary(c1, "telotraigodemipueblo"){

    Container(web_app, "Web Application", "Vue.js", "Permite ver amigos, viajes, realizar pedidos, etc")

    Container(api, "Api", "<$grails>", "Permite ver amigos, viajes, realizar pedidos, etc")

    ContainerDb(rel_db, "Relational Database", "MySQL 5.5.x", "Guarda viajes, pedidos, pagos, etc.")

    Container(filesystem, "File System", "FAT32", "Imágenes de productos organizadas por carpetas")
}

System_Ext(pasarela, "Pasarela Pagos")

Rel(anonymous_user, web_app, "Uses", "HTTPS")
Rel(aggregated_user, web_app, "Uses", "HTTPS")
Rel(administration_user, web_app, "Uses", "HTTPS")

Rel_D(web_app, api, "Uses", "HTTPS")

Rel(api, rel_db, "Reads from and writes to", "SQL/JDBC, post 3306")
Rel(web_app, filesystem, "Reads from")
Rel_D(api, pasarela, "JSON/Https", "SSL point to point")
Lay_R(rel_db, filesystem)
----
container
Figure 2. Container

Como podemos ver en este nivel especificamos tecnología (MySQL, Vue, Grails), así como tratamos por igual diferentes tipos de Containers, tanto aplicaciones como base de datos o ficheros. Mediante ContainerDb la librería nos permite personalizar mejor el container para especificar su naturaleza. Así mismo podemos ver que PlantUML nos permite añadir elementos visuales que mejoren la comprensión del sistema como por ejemplo el icono de Grails (usando una librería propia publicada en Github con iconos para Grails, Micronaut y Groovy)

Component

El tercer nivel de zoom dentro del modelo C4 detalla cada Componente o varios en el mismo diagrama si así se desea, plasmando las diferentes partes de aquel.

Por ejemplo en nuestro caso vamos a representar el Container Api mostrando sus diferentes partes y cómo se relacionan entre sí.

single_page.adoc
[plantuml]
----
!include <c4/C4_Component.puml>

title Api Backend de TelotraigodemiPueblo

Container(spa, "Single Page Application", "Vue.js", "Realiza peticiones en nombre del usuario.")

Container_Boundary(api, "API Application") {
    Component(sign, "Sign In Controller", "MVC Rest Controlle", "Allows users to sign in to the internet banking system")
    Component(accounts, "Accounts Summary Controller", "MVC Rest Controlle", "Provides customers with a summory of their bank accounts")
    Component(orders, "Orders Controller", "MVC Rest Controlle", "Provides customers with a summory of their orders")

    Component(security, "Security Component", "Spring Bean", "Provides functionality related to singing in, changing passwords, etc.")
    Component(accounts_srv, "Accounts Service", "Spring Bean", "Provides functionality related to accounts.")

    Component(gwfacade, "Banking System Facade", "Spring Bean", "A facade onto the mainframe banking system.")
}
ContainerDb(rel_db, "Relational Database", "MySQL 5.5.x", "Guarda viajes, pedidos, pagos, etc.")
System_Ext(pasarela, "Pasarela Pagos", "Realiza el cobro de pedidos.")

Rel(spa, sign, "Uses", "JSON/HTTPS")
Rel(spa, accounts, "Uses", "JSON/HTTPS")
Rel(spa, orders, "Uses", "JSON/HTTPS")

Rel(sign, security, "Uses")
Rel(security, rel_db, "Read & write to", "JDBC")

Rel(accounts, accounts_srv, "Uses")
Rel(accounts_srv, rel_db, "Read & write to", "JDBC")

Rel(orders, gwfacade, "Uses")
Rel(gwfacade, pasarela, "Uses", "XML/HTTPS")
----
single page

Code

El último nivel de detalle propuesto por C4 correspondería al de código en el cual el detalle baja hasta especificar las clases, interfaces, relaciones entre ellos etc, en un típico diagrama UML

security_service.adoc
[plantuml]
----
title Login Service

package "com.puravida.telotraigo.security" #DDDDDD {

  class SpringUserService

  class UserDetails

  class UserNotFoundException

  SpringUserService -- UserDetails : create

  SpringUserService - UserNotFoundException : throws

}
UserService ()-- SpringUserService
----
security service

Conclusión

La idea fundamental que subyace sobre todo esto es por un lado el demostrar mediante un ejemplo sencillo las posibilidades de realizar diagramas de arquitectura mediante texto versionable y por otra acercarnos a un modelo simple pero potente como es el modelo C4 donde prima la sencillez y una aproximación top-bottom

Follow comments at Telegram channel

2019 - 2020 | Mixed with Bootstrap | Baked with JBake v2.6.4