Gerando diagramas UML programaticamente com yUML e Python
Neste artigo, apresento brevemente a ferramenta yUML e como usá-la em conjunto com scripts Python para gerar um diagrama de caso de uso com base na estrutura de uma aplicação.
Neste artigo rápido, pretendo fazer uma apresentação rápida do yUML e mostrar como podemos tirar proveito da ferramenta para gerar diagramas UML da documentação de um projeto. Tudo isso de forma programática, de modo que a documentação seja reflexo da realidade do código.
yUML
O yUML é uma ferramenta de geração de diagramas UML como esboço. Ele não tem a ambição de ser ferramenta última de diagramação, mas de ajudar a criar alguns tipos de diagrama de forma facilitada. É muito útil para geração de diagramas UML-como-rascunho.
A ferramenta está disponível na forma de serviço, ou instalação on-premise. No caso do serviço, no plano gratuito, temos um namespace que permite salvar até 5 diagramas. A título de exemplo, o meu é https://yuml.me/dyegomaas/diagrams.
Mas se você não precisa ter seus diagramas salvos em nuvem, pode gerá-los via API e baixar logo em seguida. É essa abordagem que vamos explorar mais adiante.
Para criar nossos diagramas, usamos DSLs (Domain Specific Languages) especialmente projetadas para descrever esses diagramas. Três classes de diagramas são suportadas:
- Diagramas de classe
- Diagramas de casos de uso
- Diagramas de atividades
Exemplos
Não vou entrar nos detalhes das DSLs, pois é fácil encontrá-los na documentação do yUML, mas vou listar alguns exemplos, tirados do próprio site do yUML.
Diagrama de casos de uso
Um diagrama de casos de uso definido assim:
[Customer]-(Sign In)
[Customer]-(Buy Products)
(Buy Products)>(Browse Products)
(Buy Products)>(Checkout)
(Checkout)<(Add New Credit Card)
(Checkout)
[Office Staff]-(Processs Order) Gera o seguinte diagrama:

Diagrama de classes
Um diagrama de casos de uso definido assim:
// Cool Class Diagram
// ------------------
// Chain elements like this
[Customer]<>-orders*>[Order]++-0..*>[LineItem]
// Add notes
[Order]-[note: Aggregate Root ala DDD{bg:wheat}]
// Add more detail
[≪IDisposable≫;Customer|+forname: string;+surname: string;-password: string|login(user,pass)] Gera um diagrama como este:

Diagrama de atividades
Um diagrama de atividades definido assim:
(start)-|a|
|a|->(Grind Coffee)->(Pour Shot)->(Froth Milk)->(Pour Coffee)->|b|
|a|->(Fry Eggs)->(Make Toast)->(Butter Toast)->|b|
|b|-><c>[want another coffee]->(Grind Coffee)
<c>[ready to go]->(end) Gera este diagrama aqui:

Gerando um diagrama de casos de uso com yUML
O jeito mais simples para gerar um diagrama de caso de uso, é via requisição REST. Para isso, basta realizar um POST para a URL https://yuml.me/diagram/scruffy/usecase/, cujo corpo siga o seguinte formato:
{
"dsl_text": "[Usuario]-(ObterFilial),[Usuario]-(PersistirFilial)"
} Esta requisição vai retornar o nome do arquivo gerado. No meu caso, retornou b55a8893.svg, como pode ser visto na imagem abaixo:

O código do arquivo gerado é b55a8893, e com ele, podemos fazer download do diagrama gerado com um um GET em https://yuml.me/b55a8893.png. Note que eu mudei a extensão. É possível baixar nos formatos .svg, .png, .jpg, .svg e .pdf, apenas mudando a extensão.
No próximo tópico, apresento um exemplo bem simples onde usei Python para gerar um diagrama de casos de uso.
Gerando diagramas com yUML e Python
Para fins de experimento, vou utilizar aqui um exemplo do meu outro artigo sobre arquitetura gritante, mas as situações em que essa abordagem pode servir são inúmeras.
A seguir, vamos gerar um diagrama de casos de uso, mas seria igualmente simples montar um diagrama de classes lendo os fontes de um projeto.
Neste exemplo específico, tenho um projeto C# chamado Clientes.Application. O primeiro nível de pastas, a exceção do Common, descreve entidades de domínio, e o segundo nível de pastas descreve operações de negócio que afetam ou interagem com essas entidades. Cada operação dessas mapeia um caso de uso.

O objetivo então é gerar um diagrama que reflita essa estrutura. Para isso, vamos usar um simples script Python.
O processo que o script precisa realizar é o seguinte:
- Identificar os diretórios de segundo nível
- Monstar a DSL descrevendo cada um deles como Caso de Uso
- Requisitar a geração do diagrama
- Fazer download da imagem no formato desejado
A primeira etapa, é muito específica desse exemplo, então não vou entrar em detalhes, mas você pode conferir no script completo no final do artigo.
Na segunda etapa, montamos a DSL considerando que cada pasta é um caso de uso:
def build_usecase_dsl_for_directories_in(usecase_directories, user_name) -> str:
per_folder_uc = [
f'[{user_name}]-({os.path.basename(directory)})'
for directory in usecase_directories
]
return ','.join(per_folder_uc) O resultado dessa operação será algo como [Usuario]-(CasoUso1),[Usuario]-(CasoUso2).... Neste caso, cada par identifica uma relação.
No passo seguinte, podemos fazer uma requisição para o yUML para gerar nosso diagrama:
def request_usecase_diagram(usecase_dsl) -> str:
r = requests.post(
f'https://yuml.me/diagram/scruffy/usecase/',
{ "dsl_text": usecase_dsl }
)
svg_file_name = r.text
return svg_file_name Esta operação vai retornar o nome único do nosso diagrama lá no yUML. O nome do arquivo vem com a extensão .svg por padrão.
Vale notar que no caso dos diagramas de caso de uso e de classes, há três estilos visuais diferentes que podemos optar. O “scruffy” da URL acima é o mais bonitinho, mas também tem o “plain” e o “boring”.
Por fim, podemos fazer download do mesmo diagrama nos formatos .png, .jpg e .pdf.
def download_diagrams_to(target_dir, svg_file_name, desired_extensions):
if not os.path.exists(target_dir):
os.mkdir(target_dir)
for extension in desired_extensions:
file_name = svg_file_name.replace('.svg', extension)
diagram_url = f'https://yuml.me/{file_name}' # só isso já identifica o arquivo :)
print('Downloading file', diagram_url)
r = requests.get(diagram_url)
file_path = os.path.join(target_dir, f'usecase{extension}')
with open(file_path, 'wb') as output:
output.write(r.content)
output.flush()
print('Saved file', file_path) A seguir, o script na sua versão final. Não é um código bonito, nem foi feito para produção, mas é perfeitamente adequado para ser incorporado num pipeline de CI, que atualizaria o diagrama a cada novo deploy. Se novos recursos forem incluídos na aplicação, o diagrama irá contemplá-los, e pelo menos essa parte da documentação se manterá atualizada.
import os
import requests
import sys
import glob
def get_usecase_directories_in(parent_dir):
second_level_directories = glob.glob(f'{parent_dir}/*/*')
second_level_directories = list(filter(lambda f: os.path.isdir(f), second_level_directories))
usecase_directories = []
for directory in second_level_directories:
skip = False
for directory_to_exclude in ['bin', 'obj', 'Common', 'Properties']:
if (directory_to_exclude in directory):
skip = True
continue
if skip:
continue
usecase_directories.append(directory)
return usecase_directories
def build_usecase_dsl_for_directories_in(usecase_directories, user_name) -> str:
per_folder_uc = [
f'[{user_name}]-({os.path.basename(directory)})'
for directory in usecase_directories
]
return ','.join(per_folder_uc)
def request_usecase_diagram(usecase_dsl) -> str:
print('Requesting diagram generation...')
r = requests.post(
f'https://yuml.me/diagram/scruffy/usecase/',
{ "dsl_text": usecase_dsl }
)
svg_file_name = r.text
print(f'Generated diagram: {svg_file_name}')
return svg_file_name
def download_diagrams_to(target_dir, svg_file_name, desired_extensions):
if not os.path.exists(target_dir):
os.mkdir(target_dir)
for extension in desired_extensions:
file_name = svg_file_name.replace('.svg', extension)
diagram_url = f'https://yuml.me/{file_name}'
print(f'Downloading {diagram_url}...')
r = requests.get(diagram_url)
file_path = os.path.join(target_dir, f'usecase{extension}')
with open(file_path, 'wb') as output:
output.write(r.content)
output.flush()
print(f'Saved: {file_path}')
def main():
if len(sys.argv) < 2:
print('Usage: python generate_usecase_diagram.py <project_path>')
sys.exit(1)
project_path = sys.argv[1]
user_name = 'Usuario'
target_dir = './diagrams'
extensions = ['.png', '.jpg', '.pdf']
# Busca diretórios de casos de uso
usecase_dirs = get_usecase_directories_in(project_path)
if not usecase_dirs:
print('No use case directories found!')
sys.exit(1)
print(f'Found {len(usecase_dirs)} use case directories')
# Gera DSL
dsl = build_usecase_dsl_for_directories_in(usecase_dirs, user_name)
print(f'Generated DSL: {dsl}')
# Gera diagrama
svg_file = request_usecase_diagram(dsl)
# Baixa em diferentes formatos
download_diagrams_to(target_dir, svg_file, extensions)
print('Done!')
if __name__ == '__main__':
main() Conclusão
O yUML é uma ferramenta interessante para gerar diagramas UML de forma programática. Embora não seja a ferramenta mais robusta do mercado, ela serve muito bem para documentação automatizada e diagramas simples.
A principal vantagem dessa abordagem é que a documentação pode ser mantida sempre atualizada através de automação. Toda vez que a estrutura do projeto mudar, o diagrama pode ser regenerado automaticamente.
Algumas ideias para expandir essa abordagem:
- Integrar no pipeline de CI/CD
- Gerar diferentes tipos de diagrama baseados na estrutura do código
- Combinar com outras ferramentas de análise de código
- Criar dashboards com múltiplos diagramas
O script apresentado é apenas um exemplo simples, mas demonstra como é possível automatizar a geração de documentação visual de forma eficiente.