Featured image of post Mantenha seus diagramas atualizados com entrega contínua

Mantenha seus diagramas atualizados com entrega contínua

Como usar PlantUML para projetar diagramas como código e implantá-los a partir do seu Pipeline CI/CD

Introdução Link to this section

Mudar diagramas de software é difícil. O simples ato de adicionar uma nova caixa pode exigir que arrastemos todas as caixas existentes e reorganizemos o diagrama. Esta é uma das principais razões pelas quais os diagramas de software são constantemente deixados depreciados após os primeiros estágios do processo de desenvolvimento.

Neste post, mostrarei como definir diagramas como código pode ajudar a projetar e atualizar diagramas de software e como automatizar o processo de atualização da documentação com esses diagramas.

Por que criar diagramas como código? Link to this section

  • Fácil de mudar: Basta mudar o código e os elementos do diagrama são renderizados em uma boa posição (às vezes pode precisar de alguns ajustes);
  • Reutilização de código: Componentes, sprites e funções podem ser definidos e compartilhados para serem usados em outros diagramas. Podemos usar loops e condições para tornar esses trechos de código ainda mais reutilizáveis. Detalhes aqui;
  • Histórico de mudanças: Por ser código, suas versões podem ser rastreadas e comparadas com sistemas de controle de versão, como o Git, por exemplo;
  • Estilo único em todo o diagrama: A menos que explicitado, todos os elementos do diagrama terão o mesmo estilo, sem necessidade de copiar o estilo de um elemento para outro ou ter que redimensionar todas as caixas após alterar o tamanho de uma;
  • Inclusivo: Todos na equipe podem fazer checkout do código e alterá-lo sem medo porque as mudanças podem ser rastreadas e o estilo é o mesmo para todos.

PlantUML Link to this section

PlantUML é uma ferramenta de código aberto altamente personalizável que nos permite criar diagramas usando código. Apesar do nome, ele suporta muitos tipos de diagramas além de diagramas UML. Ele tem sua própria linguagem e algumas extensões para outras linguagens como AsciiMath, Creole e LaTeX.

PlantUML é uma ferramenta de linha de comando Java. Podemos executá-lo a partir da linha de comando, mas a melhor experiência é com uma extensão do Visual Studio Code.

Renderizando a partir do Visual Studio Code Link to this section

Existe uma extensão que integra o PlantUML com o Visual Studio Code.

Ele oferece realce de sintaxe e uma visualização do diagrama ao lado durante a edição e opções para exportar os diagramas do projeto atual ou de todos, além de outros recursos.

Após instalar a extensão, abra a paleta de comandos e procure por PlantUML para ver as opções disponíveis.

Renderizando a partir da linha de comando Link to this section

Primeiro, baixe o JAR compilado da página de downloads ou da página de lançamentos do GitHub.

ℹ️ Você pode querer incluir o caminho para o arquivo plantuml.jar na variável de ambiente PATH para poder usá-lo em qualquer diretório.

Em seguida, para gerar o diagrama para um arquivo de origem, basta executar o seguinte comando:

java -jar plantuml.jar Sequence.puml

Também podemos gerar os diagramas para mais de um arquivo usando padrões glob:

java -jar plantuml.jar *.puml

Diagramas C4 Link to this section

O modelo C4 é uma abordagem diferente para projetar diagramas de arquitetura de software. Eu falei sobre isso no meu post anterior.

PlantUML tem suporte nativo para Diagramas C4. Precisamos apenas incluir a biblioteca e usar seus elementos.

Aqui estão alguns exemplos:

Diagrama de Contexto do Sistema Link to this section

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@startuml C4_SystemContext

!include <C4/C4_Context>

left to right direction

Person(user, "User", "Funcionário da empresa que tem acesso ao sistema de RH")
System(hrSystem, "Sistema de RH", "Permite que os usuários gerenciem dados pessoais e contrato dos funcionários da empresa")
System_Ext(emailSystem, "Sistema de E-mail", "Responsável por enfileirar e enviar e-mails")

Rel(user, hrSystem, "Criar e alterar informações pessoais e de contrato do funcionário", "")
Rel(hrSystem, emailSystem, "Envia e-mails de notificação usando", "")

@enduml

Diagrama de Containers Link to this section

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@startuml C4_HRSystem_Containers

!define DEVICONS https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/master/devicons
!define AWSPuml https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/v14.0/dist

!include <C4/C4_Container>
!include DEVICONS/msql_server.puml
!include DEVICONS/dotnet.puml
!include AWSPuml/AWSCommon.puml
!include AWSPuml/ApplicationIntegration/SimpleQueueServiceQueue.puml

left to right direction

Person(user, "User", "Funcionário da empresa que tem acesso ao sistema de RH")

System_Boundary(hrSystem, "Sistema de RH") {
    Container(webApp, "Aplicativo Web", "ASP.NET 7 Application", "Fornece as funcionalidades do sistema através do navegador web", $sprite="dotnet")

    Container(backgroundService, "Serviço de E-mail", ".NET 7 Application", "Serviço em segundo plano que lê uma fila para alterações de dados do funcionário e envia e-mails de notificação para os funcionários", $sprite="dotnet")

    ContainerDb(database, "Banco de Dados", "SQL Server 2022", "Armazena dados de funcionários e contratos", $sprite="msql_server")

    ContainerQueue(emailQueue, "Fila", "AWS SQS", "Armazena alterações de dados do funcionário", $sprite="SimpleQueueServiceQueue")
}

System_Ext(emailSystem, "Sistema de E-mail", "Responsável por enfileirar e enviar e-mails")

Rel(user, webApp, "Criar e alterar informações pessoais e de contrato do funcionário", "")

Rel(webApp, database, "Lê / Escreve", "")
Rel(webApp, emailQueue, "Escreve notificações para", "")

Rel(backgroundService, emailQueue, "Lê notificações de", "")
Rel(backgroundService, database, "Lê dados de funcionários de", "")
Rel(backgroundService, emailSystem, "Envia e-mails usando", "")

@enduml

Diagrama de Componentes Link to this section

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@startuml C4_HRSystem_WebApp_Components

!define DEVICONS https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/master/devicons
!define AWSPuml https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/v14.0/dist

!include <C4/C4_Component>
!include DEVICONS/msql_server.puml
!include AWSPuml/AWSCommon.puml
!include AWSPuml/ApplicationIntegration/SimpleQueueServiceQueue.puml

left to right direction

Container(webApp, "Aplicativo Web", "ASP.NET 7 Application", "Fornece as funcionalidades do sistema através do navegador web", $sprite="dotnet")

Container_Boundary(webApp, "Aplicativo Web") {
    Component(employeesController, "Controlador de Funcionários", "Fornece acesso às funcionalidades relacionadas aos funcionários")

    Component(registerEmployeesUseCase, "Caso de Uso de Registro de Funcionário", "Orquestra o caso de uso de registrar um novo funcionário")

    Component(employeeDataQueueService, "Serviço de Fila de Dados de Funcionários", "Fornece funcionalidades para se comunicar com a fila")

    Component(employeeRepository, "Repositório de Funcionários", "Fornece funcionalidades para se comunicar com a tabela de banco de dados de funcionários")

    Component(loginController, "Controlador de Login", "Controlador ASP.NET Core", "Permite que os usuários se autentiquem no aplicativo web")

    Rel(employeesController, registerEmployeesUseCase, "Usa")
    Rel(registerEmployeesUseCase, employeeDataQueueService, "Usa")
    Rel(registerEmployeesUseCase, employeeRepository, "Usa")
}

ContainerDb(database, "Banco de Dados", "SQL Server 2022", "Armazena dados de funcionários e contratos", $sprite="msql_server")

ContainerQueue(emailQueue, "Fila", "AWS SQS", "Armazena alterações de dados do funcionário", $sprite="SimpleQueueServiceQueue")

Rel(employeeRepository, database, "Escreve informações do funcionário", "")
Rel(employeeDataQueueService, emailQueue, "Escreve notificações para", "")

@enduml

Mais exemplos Link to this section

Aqui estão mais exemplos do que pode ser feito com PlantUML.

Variáveis e cores Link to this section

Neste exemplo, criei um diagrama de sequência e usei variáveis para reutilizar os verbos HTTP formatados nas mensagens:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@startuml SequenceDiagram

!$get_method = "<font color=lime><b>GET</b></font>"
!$post_method = "<font color=blue><b>POST</b></font>"

participant "Frontend" as Frontend
participant "BFF" as BFF
participant "PokéAPI" as PokeAPI
database "Cache" as Cache

Frontend -> BFF : $get_method /pokemondata/{name}
BFF -> Cache : $get_method Busca por dados no cache
BFF <-- Cache : Dados em cache

alt dados não encontrados no cache
    BFF -> PokeAPI : $get_method /pokemon/{name}
    BFF <-- PokeAPI : Dados do Pokémon
    BFF -> Cache : $post_method Salva dados no cache
end

Frontend <-- BFF : Retorna dados do pokémon

@enduml

Visualização de dados JSON Link to this section

Um diagrama legal que o PlantUML pode gerar é o diagrama JSON, mostrando as propriedades e os dados de um JSON. Para gerar um diagrama JSON, basta usar os símbolos @startjson e @endjson e colar o conteúdo JSON entre eles.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@startjson JSONDiagram
{
  "abilities": [
    {
      "ability": {
        "name": "blaze",
        "url": "https://pokeapi.co/api/v2/ability/66/"
      },
      "is_hidden": false,
      "slot": 1
    },
    {
      "ability": {
        "name": "solar-power",
        "url": "https://pokeapi.co/api/v2/ability/94/"
      },
      "is_hidden": true,
      "slot": 3
    }
  ],
  "base_experience": 267,
  "forms": [
    {
      "name": "charizard",
      "url": "https://pokeapi.co/api/v2/pokemon-form/6/"
    }
  ],
  "height": 17,
  "held_items": [],
  "id": 6,
  "is_default": true,
  "location_area_encounters": "https://pokeapi.co/api/v2/pokemon/6/encounters",
  "name": "charizard",
  "order": 7,
  "past_types": [],
  "species": {
    "name": "charizard",
    "url": "https://pokeapi.co/api/v2/pokemon-species/6/"
  },
  "types": [
    {
      "slot": 1,
      "type": {
        "name": "fire",
        "url": "https://pokeapi.co/api/v2/type/10/"
      }
    },
    {
      "slot": 2,
      "type": {
        "name": "flying",
        "url": "https://pokeapi.co/api/v2/type/3/"
        }
      }
    ],
  "weight": 905
}
@endjson

Importando elementos personalizados Link to this section

PlantUML é extensível, então podemos criar ou importar elementos personalizados para usar em nossos diagramas. Um exemplo são os Ícones AWS para PlantUML. Ele tem elementos para representar a maioria dos principais serviços da AWS.

Para usá-lo, precisamos importar os elementos personalizados com o comando !import.

Neste exemplo, usei o comando !define para definir uma variável AWSPuml com o URL base e usei em todas as importações. Isso ajuda quando precisamos atualizar a versão dos objetos e também torna o código mais limpo.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@startuml InfrastructureDiagram

left to right direction

!define AWSPuml https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/v14.0/dist

!include AWSPuml/AWSCommon.puml
!include AWSPuml/NetworkingContentDelivery/CloudFront.puml
!include AWSPuml/Compute/Lambda.puml
!include AWSPuml/Storage/SimpleStorageService.puml
!include AWSPuml/Database/ElastiCache.puml

actor "User" as User

CloudFront(CloudFront, "CloudFront", "")
SimpleStorageService(S3, "S3 Bucket", "Aplicativo Angular")
Lambda(Bff, "BFF", "ASP.NET Core")
ElastiCache(Redis, "Cache", "Redis")

User --> CloudFront
CloudFront --> S3
S3 --> Bff
Bff --> Redis

@enduml

Temas Link to this section

PlantUML suporta alguns temas por padrão. Basta usar o comando !theme seguido pelo nome do tema:

1
2
3
4
@startuml
!theme materia
...
@enduml

Aqui está o diagrama de sequência anterior com o tema Materia:

Aqui podemos ver uma galeria com os temas disponíveis.

Automatizando a publicação do diagrama Link to this section

Criei um site de documentação como um exemplo de como gerar diagramas PlantUML no pipeline CI/CD. Quando os diagramas são commitados no repositório, o pipeline os renderiza como imagens e a documentação é atualizada automaticamente.

https://dgenezini.github.io/docs-sample/

Ele usa:

Para renderizar os diagramas como imagens e commitá-los no repositório, configure um novo job generate_plantuml com o código abaixo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
jobs:
  generate_plantuml:
    runs-on: ubuntu-latest
    name: plantuml
    steps:
    - name: checkout
      uses: actions/checkout@v1
      with:
        fetch-depth: 1
    - name: plantuml
      id: plantuml
      uses: grassedge/generate-plantuml-action@v1.5
      with:
        path: diagrams
        message: "Render PlantUML files"
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Em seguida, basta referenciar as imagens nos arquivos de documentação:

![](/docs-sample/diagrams/2-containers.svg)

A configuração completa do pipeline pode ser vista aqui.

O repositório de origem é aqui.

Diagrama como código 2.0 Link to this section

Simon Brown, criador do modelo C4, tem um conceito muito interessante chamado Diagrama como código 2.0 no qual um código de modelo pode gerar vários diagramas. Mais detalhes em seu post no blog.

Ele construiu uma ferramenta chamada Structurizr para isso.

💬 Like or have something to add? Leave a comment below.
Ko-fi
GitHub Sponsor
Licensed under CC BY-NC-SA 4.0
Criado com Hugo
Tema Stack desenvolvido por Jimmy