🔄 Cap 08: Diagrama de Atividades: Ações, Decisões, Concorrência e Raias
Guia Extra: Engenharia de Software e Modelagem UML • Bloco 2: Requisitos e Processos em Modelo
🗺️ Mapa Conceitual do Capítulo
flowchart TD
DA["Diagrama de Atividades (UML 2.5)\nModelagem Comportamental de Fluxos de Trabalho"] --> NODOS["Nós Estruturais e de Controle"]
NODOS --> NC_INI["Nós Terminais: Inicial (Círculo Sólido) · Final de Atividade (Olho de Boi) · Final de Fluxo (X)"]
NODOS --> NC_DEC["Nós de Decisão & Merge (Losangos com Guardas Mutuamente Exclusivas)"]
NODOS --> NC_SYNC["Sincronização Paralela: Fork (Bifurcação) & Join (Barreira de Espera)"]
NODOS --> NC_OBJ["Nós de Objeto & Pinos de Parâmetros (Fluxo de Dados)"]
DA --> PART["Partições de Atividade (Swimlanes / Raias de Responsabilidade)"]
DA --> EXC["Regiões de Atividade Interrompível (Tratamento de Exceções & Cancelamento)"]
DA --> SEMANTICA["Semântica Formal de Tokens (Redes de Petri)"]
style DA fill:#ede7f6,stroke:#512da8,stroke-width:2px
style NODOS fill:#e1f5fe,stroke:#0288d1,stroke-width:2px
style NC_SYNC fill:#fff3e0,stroke:#ff9800,stroke-width:2px
style PART fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
style SEMANTICA fill:#fce4ec,stroke:#e91e63,stroke-width:2px
🔄 1. Propósito e Semântica Formal Baseada em Tokens
O Diagrama de Atividades da UML é a ferramenta padrão para modelagem de aspectos dinâmicos e comportamentais de sistemas de software:
- Detalhar passo a passo a lógica procedimental de um Caso de Uso complexo (Capítulo 06).
- Modelar algoritmos concorrentes e fluxos de dados entre componentes de software.
- Expressar regras de negócio e fluxos de tarefas distribuídas entre múltiplos atores.
Diferente de um simples fluxograma clássico de engenharia, a semântica da UML 2.5 para o Diagrama de Atividades é herdada diretamente da teoria matemática das Redes de Petri: o fluxo é compreendido como a circulação de Tokens (Fichas de Controle e de Dados) através de uma rede de nós.
- Um nó de ação só é ativado quando todos os tokens necessários chegam aos seus fluxos de entrada.
- Ao concluir a computação da ação, o nó consome as fichas de entrada e emite novas fichas para seus fluxos de saída.
🧱 2. Primitivas Estruturais e Nós de Controle
| Elemento | Símbolo Visual UML | Semântica de Tokens | Diferença Crítica e Armadilha Comum |
|---|---|---|---|
| Nó Inicial | Círculo preenchido sólido | Gera o token primordial no início da atividade. | Uma atividade pode ter múltiplos nós iniciais em cenários de disparo concorrente. |
| Final de Atividade | Círculo vazado com círculo sólido dentro (Olho de Boi) | Destrói todos os tokens da atividade inteira imediatamente. | Se um token atingir este nó, até mesmo ramos paralelos ativos em execução são sumariamente abortados. |
| Final de Fluxo | Círculo com um X central |
Destrói apenas o token daquele ramo. | Usado quando um ramo concorrente termina mas outros ramos devem continuar vivos. |
| Nó de Ação | Retângulo de cantos arredondados | Executa uma tarefa elementar atômica. | Representa trabalho do sistema ou humano (ex: “Validar Token JWT”). |
| Decisão | Losango com 1 entrada e $N$ saídas | Encaminha o token para estritamente uma saída via Guardas [...]. |
As guardas devem ser mutuamente exclusivas e exaustivas (use [else]). |
| Merge (Mesclagem) | Losango com $N$ entradas e 1 saída | Reúne caminhos alternativos sem sincronização. | O primeiro token que chega prossegue imediatamente; não há barreira de espera. |
| Fork (Bifurcação) | Barra sólida com 1 entrada e $N$ saídas | Duplica o token, disparando $N$ fluxos concorrentes. | Inicia paralelismo real (threads ou goroutines). |
| Join (Junção) | Barra sólida com $N$ entradas e 1 saída | Barreira de Sincronização: espera tokens de todos os ramos. | Se um dos ramos nunca produzir token, o join fica em deadlock permanente. |
⚡ 3. Concorrência: A Diferença Vital entre Decisão e Fork
Uma das falhas mais frequentes em provas de certificação e na prática de engenharia é confundir nós de decisão com nós de paralelismo:
graph TD
subgraph DECISAO["Decisão (Roteamento Exclusivo OR)"]
D1["Entrada"] --> L1{"Losango"}
L1 -->|"[saldo >= valor]"| D2["Debitar Conta"]
L1 -->|"[else]"| D3["Rejeitar Compra"]
end
subgraph FORK_JOIN["Fork & Join (Concorrência Real AND)"]
F1["Entrada"] --> B1["=== BARRA FORK ==="]
B1 --> F2["Emitir Nota Fiscal"]
B1 --> F3["Separar Produto no Estoque"]
F2 --> B2["=== BARRA JOIN ==="]
F3 --> B2
B2 --> F4["Despachar Pedido"]
end
- No Losango de Decisão: O token de entrada segue apenas um único caminho avaliado pela expressão de guarda booleana.
- Na Barra de Fork: O token de entrada é clonado em paralelo para todos os caminhos de saída, executando as ações simultaneamente. A barra de Join atua como uma barreira que só libera o fluxo seguinte quando todas as ações concorrentes tiverem finalizado.
🏊 4. Partições de Atividade (Swimlanes) e Fluxo de Objetos
As Partições (Raias) organizam as ações de acordo com o responsável pela sua execução (Departamentos corporativos, Microsserviços ou Camadas arquiteturais):
- Nó de Objeto (Object Node): Representado por um retângulo tradicional de cantos retos, indica dados físicos ou conceituais que fluem entre ações (ex:
Pedido [Criado]$\rightarrow$Pedido [Faturado]). - Pinos de Ação (Pins): Pequenos quadrados nas bordas dos nós de ação que representam os parâmetros de entrada e valores de retorno das funções.
💻 5. Mapeamento Direto para Linguagens de Programação
O Diagrama de Atividades mapeia diretamente para estruturas de controle em código-fonte moderno:
// Exemplo de Mapeamento em Go: Fork + Join com sync.WaitGroup
func ProcessarCompraConcorrente(pedido Pedido) error {
var wg sync.WaitGroup
erros := make(chan error, 2)
wg.Add(2) // Fork: 2 ramos paralelos
// Ramo 1: Emissão de Nota Fiscal
go func() {
defer wg.Done()
if err := emitirNotaFiscal(pedido); err != nil {
erros <- err
}
}()
// Ramo 2: Reserva no Almoxarifado
go func() {
defer wg.Done()
if err := reservarEstoque(pedido); err != nil {
erros <- err
}
}()
wg.Wait() // Join: Barreira de sincronização
close(erros)
// Avaliação de Merge e Decisão
for err := range erros {
if err != nil {
return err // Final com Erro
}
}
return despacharPedido(pedido) // Final de Sucesso
}