Exemplo de diagrama de classes UML: modelando uma plataforma de cursos online

Um diagrama de classes UML resolvido para uma plataforma de cursos online: classes, visibilidade, multiplicidade e quando usar associação, composição ou herança.

· Fundador e desenvolvedor

Publicado em

Exemplo de diagrama de classes UML: modelando uma plataforma de cursos online

A maioria dos tipos de diagrama UML vive nos livros didáticos. O diagrama de classes é o que os engenheiros ainda desenham: uma imagem das coisas de que um sistema trata, do que cada uma guarda, do que ela sabe fazer e de como elas se relacionam. Bem desenhado, ele encerra discussões sobre o modelo de domínio antes que alguém escreva uma migração ou uma API.

Este artigo constrói um diagrama de classes UML para uma pequena plataforma de cursos online, uma decisão por vez, e explica a notação à medida que ela aparece: visibilidade, multiplicidade, associação, composição, herança e interfaces. No fim, mostra os erros que tornam diagramas de classes inúteis e dá uma olhada rápida em como o diagrama vira código.

Quando o diagrama de classes é a imagem certa

A especificação UML 2.5.1 divide seus diagramas em diagramas de estrutura e diagramas de comportamento. O diagrama de classes é um diagrama de estrutura: mostra tipos, seus atributos e operações e os relacionamentos entre eles, sem nenhuma noção de tempo.

Pergunta que você precisa responder Diagrama
Quais conceitos existem, o que sabem, como se relacionam Diagrama de classes
Como os dados são armazenados: tabelas, chaves, chaves estrangeiras Diagrama ER
O que acontece, e em que ordem, entre participantes Diagrama de sequência
Por quais estados um objeto passa Diagrama de máquina de estados

Um diagrama de classes vale a pena para um modelo de domínio, para os tipos públicos de uma biblioteca ou para um design que você está prestes a construir. Não vale a pena como despejo de todas as classes de um repositório: uma IDE mostra isso melhor, e a imagem fica desatualizada no próximo commit.

A notação em duas tabelas

Uma classe é uma caixa com até três compartimentos: o nome, os atributos e as operações. Qualquer compartimento pode ser omitido.

Elemento Notação Significado
Classe abstrata Nome em itálico, ou {abstract} Não pode ser instanciada; as subclasses a completam
Interface «interface» acima do nome Um contrato de operações
Visibilidade + public, - private, # protected, ~ package Quem pode usar o recurso
Atributo name: Type [multiplicity] = default Os dados que o objeto guarda
Operação name(param: Type): ReturnType O comportamento que o objeto oferece
Recurso estático Sublinhado Pertence à classe, não a uma instância

Relacionamentos são linhas entre classes, e as pontas carregam a maior parte do significado:

Relacionamento Linha Leia como
Associação Linha contínua; uma ponta de seta aberta marca uma ponta navegável "está relacionado a"
Agregação Linha contínua, losango vazio no todo "agrupa"; as partes vivem por conta própria
Composição Linha contínua, losango preenchido no todo "é feito de"; as partes vão embora com o todo
Generalização Linha contínua, triângulo vazio no pai "é um tipo de"
Realização Linha tracejada, triângulo vazio na interface "implementa"
Dependência Linha tracejada, ponta de seta aberta no destino "usa"; uma mudança no destino pode quebrar a origem

A composição é a rígida. Pela especificação, um objeto parte pertence a no máximo um composto por vez e, quando o composto é excluído, suas partes são excluídas junto. A agregação compartilhada, o losango vazio, é a vaga: a própria especificação diz que sua semântica precisa varia conforme a área de aplicação e o modelador. Use-a só se a sua equipe concordar sobre o que ela significa; uma associação simples diz o mesmo com menos debate.

Multiplicidade

A multiplicidade diz quantos objetos podem estar em uma ponta de uma associação: 1 para exatamente um, 0..1 para opcional, * (o mesmo que 0..*) para qualquer quantidade, 1..* para pelo menos um e intervalos como 2..5. Escreva-a na ponta, ao lado da classe que ela conta. Leia Course 1 — 1..* Module como "um curso tem um ou mais módulos, e cada módulo pertence a exatamente um curso".

Multiplicidades ausentes são o motivo mais comum para um diagrama de classes começar discussões em vez de encerrá-las. No exemplo abaixo, todas as associações têm as duas pontas preenchidas.

O exemplo: uma plataforma de cursos online

Comece por frases simples, não por caixas:

  1. Instrutores publicam cursos. Um curso tem um ou mais instrutores.
  2. Um curso é feito de módulos ordenados, e um módulo de aulas ordenadas.
  3. Uma aula é um vídeo, um quiz ou uma tarefa.
  4. Quizzes e tarefas recebem nota; vídeos não.
  5. Alunos se matriculam em cursos. Uma matrícula acompanha o progresso e guarda as entregas do aluno.
  6. Um aluno que conclui um curso recebe um certificado por aquela matrícula.
  7. Os cursos são agrupados em categorias, e um curso pode estar em várias.

Usuários: uma classe, dois papéis

O primeiro impulso é fazer de Student e Instructor subclasses de User. Isso quebra na primeira vez que um instrutor faz o curso de outra pessoa: na maioria das linguagens de programação, um objeto tem uma única classe a vida toda, então não pode ser as duas coisas nem mudar depois. Modele os papéis como associações: um User ministra cursos, e um User tem matrículas. Reserve a herança para o que é um tipo de algo a vida toda, como um quiz ser um tipo de aula.

Composição quando os ciclos de vida estão ligados

Um módulo não significa nada fora do seu curso, nem uma aula fora do seu módulo. Excluir um curso exclui os dois. Isso é composição: um losango preenchido em Course e em Module, com 1 no todo. Categorias são diferentes. Excluir a categoria "Engenharia de dados" não pode excluir seus cursos, então de Category para Course há uma agregação com * nas duas pontas.

Herança mais uma interface

Lesson é abstrata; VideoLesson, Quiz e Assignment herdam seu título e sua posição. A avaliação atravessa essa árvore: quizzes e tarefas recebem nota, vídeos não, e uma revisão por pares adicionada no ano que vem poderia receber nota sem ser uma aula. Isso é uma interface, Gradable, realizada por Quiz e Assignment. GradingService depende de Gradable, não das classes concretas, e a seta de dependência tracejada diz exatamente isso.

Uma associação com dados próprios

Enrollment fica entre User e Course. A UML tem uma notação própria para isso, a classe de associação, ligada à linha por uma linha tracejada. A maioria das equipes a desenha como uma classe comum com duas associações, porque ela tem identidade, dados e comportamento próprios: a data da matrícula, o progresso, as entregas e no máximo um certificado.

Course é composto de Modules, que são compostos de Lessons. VideoLesson, Quiz e Assignment herdam da classe abstrata Lesson; Quiz e Assignment realizam a interface Gradable. Os usuários ministram cursos e se matriculam via Enrollment, que guarda Submissions e um Certificate opcional.

32 componentes · 16 conexões

Abrir no Archboard
Conteúdo do diagrama
  • Category
  • Course
  • User
  • Module
  • Enrollment
  • Certificate
  • Lesson
  • Submission
  • VideoLesson
  • Quiz
  • Assignment
  • Gradable
  • GradingService
  • groups
  • teaches
  • enrolls
  • for
  • earns
  • holds
  • answers
  • «use»
Um diagrama de classes UML para uma plataforma de cursos online: cursos compostos de módulos e aulas, três tipos de aula sob uma classe abstrata, trabalhos avaliados por trás de uma interface e matrículas que ligam usuários a cursos.

Como ler:

  • No alto à esquerda, o catálogo. Course é composto de Module, que é composto de Lesson, com losangos preenchidos no todo. Category agrupa cursos com um losango vazio.
  • Embaixo à esquerda, os tipos de aula. Lesson é abstrata. VideoLesson, Quiz e Assignment apontam para ela com triângulos vazios.
  • À direita, o aprendizado. Um User ministra cursos, com 1..* instrutores por curso, e tem qualquer número de matrículas, cada uma para exatamente um curso. Um Enrollment guarda entregas e rende no máximo um Certificate.
  • Embaixo à direita, a avaliação. Quiz e Assignment realizam Gradable com linhas tracejadas e triângulos vazios. Cada Submission responde a um item avaliável, e GradingService depende apenas da interface.

Erros comuns

  • Todas as classes do código. De dez a quinze classes por diagrama é um bom limite. Divida um modelo maior por área ou mostre o resto como nomes sem compartimentos.
  • Getters e setters como operações. Liste operações com significado de domínio, como publish() ou enroll(), e não os métodos de acesso.
  • Tabelas desenhadas como classes. Atributos como courseId pertencem ao diagrama ER. Num diagrama de classes, a linha de associação é a referência.
  • Herança para papéis. Se um objeto pode ganhar ou perder o papel, é uma associação, não uma subclasse.
  • Losangos sem significado. Se ninguém sabe dizer o que excluir o todo faz com as partes, use uma associação simples.
  • Detalhes de implementação no nível de domínio. Deixe de fora anotações de framework, DTOs e repositórios, a menos que o diagrama seja sobre eles.

Do diagrama ao código

O diagrama vira código diretamente. Em TypeScript:

interface Gradable {
  maxScore(): number;
  autoGrade(submission: Submission): Score | null;
}

abstract class Lesson {
  constructor(public title: string, public position: number) {}
}

class Quiz extends Lesson implements Gradable {
  questions: Question[] = [];
  maxScore(): number {
    return this.questions.length;
  }
  autoGrade(submission: Submission): Score | null {
    return submission.scoreAgainst(this.questions);
  }
}

class Course {
  private readonly modules: Module[] = []; // composition: owned and removed with the course
  categories: Category[] = []; // aggregation: categories exist on their own

  addModule(title: string): Module {
    const created = new Module(title, this.modules.length);
    this.modules.push(created);
    return created;
  }
}

A composição vira posse: o curso cria seus módulos, e nada mantém um módulo vivo depois que o curso deixa de existir. A agregação vira uma referência a um objeto com ciclo de vida próprio. Quando o código e o diagrama divergirem, atualize o diagrama no mesmo pull request; isso é bem mais fácil quando o diagrama é um link e não uma captura de tela, como descrito em diagramas de arquitetura que não ficam desatualizados.

Monte no Archboard

  1. Entre com Google ou GitHub e crie um diagrama.
  2. Do pack UML, adicione uma Classe para cada conceito e escreva atributos e operações, um por linha. Defina o estereótipo de Lesson como abstract.
  3. Adicione uma Interface para Gradable com suas duas operações.
  4. Conecte as classes. Defina a ponta de seta do lado do todo como Losango preenchido para composição e Losango para agregação, e como Triângulo do lado do pai para herança. Desenhe a realização como uma linha tracejada com um Triângulo na interface, e a dependência como uma linha tracejada com uma Seta aberta.
  5. Coloque as multiplicidades como texto ao lado de cada ponta e nomeie as associações, como "teaches", no rótulo da conexão.
  6. Adicione uma Nota UML para decisões, como por que os papéis são associações.

O exemplo usa 13 nós de diagrama e fica dentro do plano Free. Com um agente de IA conectado via MCP, você pode descrever os requisitos e deixar que ele esboce as classes, e depois corrigir as multiplicidades você mesmo.

Perguntas frequentes

Qual a diferença entre um diagrama de classes e um diagrama de objetos? Um diagrama de classes mostra tipos e as regras entre eles. Um diagrama de objetos mostra instâncias específicas em um momento, com seus valores e vínculos, e serve para conferir um diagrama de classes contra um exemplo real.

Qual a diferença entre agregação e composição? Na composição, uma parte pertence a um único todo e é excluída com ele. Na agregação, as partes podem existir por conta própria e ser compartilhadas; seu significado exato fica a critério do modelador, por isso muitas equipes usam uma associação simples no lugar.

Um diagrama de classes deve corresponder exatamente ao código? Um modelo de domínio deve corresponder aos conceitos e relacionamentos, não a cada campo e método. Mantenha-o num nível que tanto uma gerente de produto quanto um engenheiro consigam ler.

Como um diagrama de classes difere de um diagrama de componentes C4? Um diagrama de componentes C4 mostra blocos implantáveis e suas responsabilidades. Um diagrama de classes desce um nível, até os tipos dentro deles; veja o exemplo de diagrama do modelo C4.

Posso gerar um diagrama de classes automaticamente a partir do código? Muitas IDEs conseguem, mas o resultado mostra todas as classes e é difícil de ler. Desenhe o modelo de domínio à mão ou com um agente de IA e limite-o às classes que importam.

Comece grátis — um assento, sem cartão de crédito.

Artigos relacionados

← Voltar ao blog

Abra um canvas em um minuto

Entre com Google ou GitHub. Grátis para um assento. Faça upgrade quando a equipe entrar.