Grátis · Sem cadastro · Funciona com arquivos .mmd

Editor de diagrama de classes Mermaid

Um diagrama de classes mostra tipos e como eles se relacionam: o que contém o quê, o que herda de quê, o que depende de quê. Use quando o assunto for o formato do código — um modelo de domínio, uma interface de extensão, uma árvore de herança. Se quiser mostrar o que acontece em tempo de execução em vez de como os tipos se encaixam, use um diagrama de sequência.

Um modelo de domínio de pagamentos

Três tipos de relação num diagrama só: composição para as partes que não sobrevivem ao todo, herança para a hierarquia de formas de pagamento e uma associação comum com multiplicidade. Aqui o significado está quase todo nas setas; as caixas de classe são quase acessórias.

classDiagram
    class Pedido {
        +String codigo
        +StatusPedido status
        +Dinheiro total()
        +void adicionarItem(Produto p, int quantidade)
    }
    class ItemDoPedido {
        +Produto produto
        +int quantidade
        +Dinheiro subtotal()
    }
    class FormaDePagamento {
        <<abstract>>
        +autorizar(Dinheiro valor) bool
    }
    class CartaoDeCredito {
        +String ultimos4
        +autorizar(Dinheiro valor) bool
    }
    class Pix {
        +String chave
        +autorizar(Dinheiro valor) bool
    }
    class Boleto {
        +String linhaDigitavel
        +autorizar(Dinheiro valor) bool
    }

    Pedido "1" *-- "1..*" ItemDoPedido : contém
    Pedido --> FormaDePagamento : pago com
    FormaDePagamento <|-- CartaoDeCredito
    FormaDePagamento <|-- Pix
    FormaDePagamento <|-- Boleto
Abrir isto no editor
Publicidade

Exemplos resolvidos

1. Uma classe

`+` é público, `-` é privado e `#` é protegido. Um membro com parênteses é desenhado como método; sem eles é um campo.

classDiagram
    class Usuario {
        +String email
        -String hashDaSenha
        +bool verificar(String candidata)
    }
Abrir no editor

2. Herança e interfaces

`<|--` é herança, lida como «o da direita estende o da esquerda». A anotação `<<interface>>` é um rótulo e não um comportamento, mas é ela que torna o diagrama legível.

classDiagram
    class Repositorio {
        <<interface>>
        +buscar(String id) Entidade
        +salvar(Entidade e) void
    }
    class RepositorioPostgres {
        -Conexao conexao
        +buscar(String id) Entidade
        +salvar(Entidade e) void
    }
    class RepositorioEmMemoria {
        -Map armazenamento
        +buscar(String id) Entidade
        +salvar(Entidade e) void
    }
    Repositorio <|.. RepositorioPostgres
    Repositorio <|.. RepositorioEmMemoria
Abrir no editor

3. Composição versus agregação

A diferença é o tempo de vida. O losango preenchido (`*--`) significa que a parte morre com o todo: apague a nota fiscal e os itens dela somem. O losango vazado (`o--`) significa que a parte continua existindo por conta própria.

classDiagram
    class NotaFiscal {
        +String numero
    }
    class ItemDaNota {
        +String descricao
    }
    class Cliente {
        +String razaoSocial
    }
    NotaFiscal "1" *-- "1..*" ItemDaNota : composta por
    Cliente "1" o-- "0..*" NotaFiscal : emitiu
Abrir no editor

4. Genéricos

Os tis dão parâmetros de tipo: `Repositorio~Usuario~`. Aninhar também funciona, o que às vezes é necessário e raramente é boa ideia. Acentos funcionam normalmente dentro de um genérico.

classDiagram
    class Repositorio~T~ {
        +buscar(String id) T
        +todos() List~T~
    }
    class Cache~K, V~ {
        +obter(K chave) V
        +guardar(K chave, V valor) void
    }
    class RepositorioDeUsuarios {
        +buscarPorEmail(String email) Usuario
    }
    Repositorio~Usuario~ <|-- RepositorioDeUsuarios
Abrir no editor

5. Notas e direção

`direction LR` dispõe o diagrama da esquerda para a direita, o que costuma servir melhor a uma árvore de herança que o padrão. Uma nota é o lugar certo para a restrição que não cabe numa caixa de classe.

classDiagram
    direction LR
    class RepositorioDeEventos {
        +anexar(Evento e) void
        +reproduzir(String fluxo) List~Evento~
    }
    class Snapshot {
        +int versao
        +byte[] conteudo
    }
    RepositorioDeEventos --> Snapshot : grava a cada 100 eventos
    note for RepositorioDeEventos "Somente anexação. Eventos nunca são alterados nem apagados."
Abrir no editor

Referência de sintaxe do diagrama de classes

As setas de relação são a parte que vale decorar: são elas que separam um diagrama de classes de um desenho de caixas e linhas, e se leem da direita para a esquerda de um jeito que confunde por bastante tempo.

SintaxeSignificado
classDiagramAbre o diagrama. Diferencia maiúsculas.
class Nome { ... }Classe com membros. A chave de fechamento fica numa linha só dela.
+membroPúblico.
-membroPrivado.
#membroProtegido.
+metodo(Tipo arg) TipoRetornoUm método — são os parênteses que o tornam um.
<<interface>> / <<abstract>>Estereótipo, escrito na primeira linha dentro da classe.
A <|-- BHerança: B estende A.
A <|.. BRealização: B implementa a interface A.
A *-- BComposição: B não sobrevive a A.
A o-- BAgregação: B pode existir sem A.
A --> BAssociação com direção.
A ..> BDependência — A usa B mas não o guarda.
A "1" --> "0..*" B : rótuloMultiplicidade em cada ponta mais um rótulo de relação.
class Repo~T~Parâmetro de tipo genérico.
note for A "texto"Nota anexada a uma classe.
direction LRMuda a direção do layout.
Publicidade

Erros que quebram um diagrama de classes

Reproduzidos no Mermaid 11.12.2. O diagrama de classes é dos mais tolerantes entre os seis tipos daqui, então vários destes desenham tranquilamente e devolvem a imagem errada.

O que você vê

Parse error, terminando em: got 'EOF_IN_STRUCT'

Por quê

Um corpo de classe aberto com `{` e nunca fechado. Por uma vez o nome do token ajuda de verdade: significa que o arquivo terminou enquanto ainda estávamos dentro de uma classe.

Solução

Feche a chave numa linha só dela.

Quebrado
classDiagram
    class Pedido {
        +String codigo
Corrigido
classDiagram
    class Pedido {
        +String codigo
    }

O que você vê

Parse error, terminando em: got 'ANNOTATION_END'

Por quê

Uma seta de diagrama de sequência usada num diagrama de classes. `->>` não significa nada aqui, e o analisador entra o bastante nela para produzir um nome de token confuso.

Solução

Use uma relação de classes: `-->` para associação, `<|--` para herança, `*--` para composição.

Quebrado
classDiagram
    Pedido ->> Cliente
Corrigido
classDiagram
    Pedido --> Cliente : pertence a

O que você vê

No diagram type detected matching given configuration

Por quê

Maiúsculas erradas na palavra-chave. `classdiagram` não é `classDiagram`.

Solução

D maiúsculo.

Quebrado
classdiagram
    class Pedido
Corrigido
classDiagram
    class Pedido

O que você vê

A seta aponta para o lado contrário do que você queria

Por quê

As setas de relação se leem da ponta para trás. `A <|-- B` significa que B herda de A, não o contrário. Escrita ao contrário ela desenha do mesmo jeito: só que agora afirma que sua classe base estende a própria subclasse.

Solução

Leia como «a ponta distante estende a ponta com a seta». Ponha o pai à esquerda de `<|--`.

Quebrado
classDiagram
    CartaoDeCredito <|-- FormaDePagamento
Corrigido
classDiagram
    FormaDePagamento <|-- CartaoDeCredito

O que você vê

Aparece um campo onde você esperava um método

Por quê

Os parênteses são a única coisa que distingue um método de um campo. `+salvar` é um campo chamado salvar; `+salvar()` é um método. Os dois são válidos, então nada avisa.

Solução

Acrescente os parênteses e, depois deles, o tipo de retorno se quiser vê-lo.

Quebrado
classDiagram
    class Repo {
        +salvar
        +buscar
    }
Corrigido
classDiagram
    class Repo {
        +salvar(Entidade e) void
        +buscar(String id) Entidade
    }

O que você vê

Composição e agregação parecem iguais de relance e significam o oposto

Por quê

`*--` e `o--` diferem em um caractere e codificam uma diferença semântica real: se a parte pode sobreviver ao todo. Usar o errado produz um diagrama tecnicamente bem formado e factualmente falso sobre o seu domínio.

Solução

Losango preenchido `*--` quando apagar o pai apaga o filho. Vazado `o--` quando não.

Quebrado
classDiagram
    Pedido o-- ItemDoPedido : contém
Corrigido
classDiagram
    Pedido *-- ItemDoPedido : contém

Notas sobre a renderização

Medido no Mermaid 11.12.2 que este site usa.

Cresce em altura mais rápido que qualquer outro tipo daqui

Medido com classes de dois membros encadeadas por herança: três classes dão um viewBox de aproximadamente 176×548 e quarenta classes dão 180×7726, ou seja, cerca de 194 pixels de altura por classe, o crescimento mais íngreme dos seis tipos do site. Um diagrama de quarenta classes passa dos sete mil pixels de altura e é inservível como imagem única. `direction LR` ajuda, mas depois de umas quinze classes a solução honesta é dividir o diagrama por contexto.

A quantidade de membros quase não afeta a largura

A largura é ditada pela assinatura de membro mais longa, não por quantos membros existem. Uma classe com vinte campos curtos não é mais larga que uma com três. Ou seja: pode ser generoso com membros e mesquinho com classes, o oposto do que o instinto pede. Em português as assinaturas saem mais longas que em inglês, então a largura costuma ser decidida por um único método de nome descritivo.

Acentos, til e cedilha funcionam em toda parte

Verificado: `código`, `hashDaSenha` e `ItemDoPedido` funcionam como nomes de classe e de membro, e os acentos passam também dentro de um genérico. Não é preciso tirar os acentos do modelo para ele desenhar. A única restrição real vem do til usado como sintaxe de genérico.

Genéricos usam tis, e isso tem consequência

`Repositorio~T~` existe porque os sinais de menor e maior colidiriam com o HTML dos rótulos. Também implica que um til literal num nome de classe ou de membro será lido como o começo de um parâmetro de tipo. É raro, mas genuinamente confuso quando acontece.

Os rótulos são HTML, então a exportação para PNG redesenha

Os rótulos de classe são desenhados dentro de um `<foreignObject>` do SVG, que os navegadores se recusam a rasterizar num canvas. A exportação para PNG deste site falhava em silêncio e devolvia um arquivo SVG; agora ela redesenha antes o diagrama com rótulos em texto SVG simples. O PNG sai correto e em tamanho cheio, com uma tipografia muito levemente diferente da tela.

Quando usar outro diagrama

Se você está documentando um banco de dados e não um sistema de tipos, use um diagrama ER. A distinção importa: diagramas de classes modelam comportamento e herança, que tabelas não têm, e diagramas ER modelam chaves e cardinalidade direito, o que os de classes disfarçam.

Se o diagrama é basicamente caixas com `-->` entre elas e sem membros, o que você está desenhando é uma arquitetura, não um diagrama de classes. Um fluxograma com subgrafos fica melhor e afirma menos.

E se a lista de classes é gerada a partir do código, considere se o diagrama também não deveria ser. Um diagrama de classes mantido à mão para uma base que muda toda semana está errado em um mês, e um diagrama errado custa mais caro que diagrama nenhum.

Outros tipos de diagrama

Escrito por Dominik Malsch · Última atualização:

Abrir o editor →