Ejemplo de diagrama de clases UML: cómo modelar una plataforma de cursos en línea

Un diagrama de clases UML resuelto para una plataforma de cursos en línea: clases, visibilidad, multiplicidad y cuándo usar asociación, composición, herencia o interfaz.

· Fundador y desarrollador

Publicado el

Ejemplo de diagrama de clases UML: cómo modelar una plataforma de cursos en línea

La mayoría de los tipos de diagrama UML viven en los libros de texto. El diagrama de clases es el que los ingenieros siguen dibujando: una imagen de las cosas de las que trata un sistema, qué guarda cada una, qué puede hacer y cómo se relacionan. Bien dibujado, zanja las discusiones sobre el modelo de dominio antes de que nadie escriba una migración o una API.

Este artículo construye un diagrama de clases UML para una pequeña plataforma de cursos en línea, una decisión cada vez, y explica la notación a medida que aparece: visibilidad, multiplicidad, asociación, composición, herencia e interfaces. Termina con los errores que hacen inútiles los diagramas de clases y con un vistazo rápido a cómo el diagrama se traslada al código.

Cuándo un diagrama de clases es la imagen correcta

La especificación UML 2.5.1 divide sus diagramas en diagramas de estructura y diagramas de comportamiento. Un diagrama de clases es un diagrama de estructura: muestra tipos, sus atributos y operaciones y las relaciones entre ellos, sin ninguna noción de tiempo.

Pregunta que necesitas responder Diagrama
Qué conceptos existen, qué saben y cómo se relacionan Diagrama de clases
Cómo se almacenan los datos: tablas, claves, claves foráneas Diagrama ER
Qué ocurre y en qué orden entre participantes Diagrama de secuencia
Por qué estados pasa un objeto Diagrama de máquina de estados

Un diagrama de clases se gana su sitio para un modelo de dominio, para los tipos públicos de una biblioteca o para un diseño que estás a punto de construir. No se lo gana como volcado de todas las clases de un repositorio: eso lo muestra mejor un IDE, y la imagen queda desfasada con el siguiente commit.

La notación en dos tablas

Una clase es una caja con hasta tres compartimentos: el nombre, los atributos y las operaciones. Cualquier compartimento puede omitirse.

Elemento Notación Significado
Clase abstracta Nombre en cursiva, o {abstract} No se puede instanciar; las subclases la completan
Interfaz «interface» sobre el nombre Un contrato de operaciones
Visibilidad + public, - private, # protected, ~ package Quién puede usar la característica
Atributo name: Type [multiplicity] = default Los datos que guarda el objeto
Operación name(param: Type): ReturnType El comportamiento que ofrece el objeto
Característica estática Subrayada Pertenece a la clase, no a una instancia

Las relaciones son líneas entre clases, y los extremos llevan la mayor parte del significado:

Relación Línea Se lee como
Asociación Línea continua; una punta de flecha abierta marca un extremo navegable "está relacionado con"
Agregación Línea continua, rombo hueco en el todo "agrupa"; las partes viven por su cuenta
Composición Línea continua, rombo relleno en el todo "está hecho de"; las partes desaparecen con el todo
Generalización Línea continua, triángulo hueco en el padre "es un tipo de"
Realización Línea discontinua, triángulo hueco en la interfaz "implementa"
Dependencia Línea discontinua, punta de flecha abierta en el destino "usa"; un cambio en el destino puede romper el origen

La composición es la estricta. Según la especificación, un objeto parte pertenece como mucho a un compuesto a la vez, y cuando se elimina el compuesto, sus partes se eliminan con él. La agregación compartida, el rombo hueco, es la ambigua: la propia especificación dice que su semántica precisa varía según el ámbito de aplicación y el modelador. Úsala solo si tu equipo se pone de acuerdo en lo que significa; una asociación simple dice lo mismo con menos debate.

Multiplicidad

La multiplicidad indica cuántos objetos pueden estar en un extremo de una asociación: 1 para exactamente uno, 0..1 para opcional, * (lo mismo que 0..*) para cualquier número, 1..* para al menos uno y rangos como 2..5. Escríbela en el extremo, junto a la clase que cuenta. Lee Course 1 — 1..* Module como "un curso tiene uno o más módulos, y cada módulo pertenece exactamente a un curso".

Las multiplicidades que faltan son la razón más habitual de que un diagrama de clases empiece discusiones en lugar de cerrarlas. En el ejemplo de abajo, todas las asociaciones tienen sus dos extremos completos.

El ejemplo: una plataforma de cursos en línea

Empieza por frases sencillas, no por cajas:

  1. Los instructores publican cursos. Un curso tiene uno o más instructores.
  2. Un curso está hecho de módulos ordenados, y un módulo de lecciones ordenadas.
  3. Una lección es un vídeo, un cuestionario o una tarea.
  4. Los cuestionarios y las tareas se califican; los vídeos no.
  5. Los estudiantes se inscriben en cursos. Una inscripción registra el progreso y guarda las entregas del estudiante.
  6. Un estudiante que completa un curso obtiene un certificado por esa inscripción.
  7. Los cursos se agrupan en categorías, y un curso puede estar en varias.

Usuarios: una clase, dos roles

El primer impulso es hacer Student e Instructor subclases de User. Eso se rompe la primera vez que un instructor sigue el curso de otra persona: en la mayoría de los lenguajes de programación, un objeto tiene una sola clase durante toda su vida, así que no puede ser ambas cosas ni cambiar después. Modela los roles como asociaciones: un User imparte cursos y un User tiene inscripciones. Reserva la herencia para lo que es un tipo de algo durante toda su vida, como un cuestionario que es un tipo de lección.

Composición cuando los ciclos de vida van unidos

Un módulo no significa nada fuera de su curso, ni una lección fuera de su módulo. Borrar un curso borra ambos. Eso es composición: un rombo relleno en Course y en Module, con 1 en el todo. Las categorías son distintas. Borrar la categoría "Ingeniería de datos" no debe borrar sus cursos, así que de Category a Course hay una agregación con * en ambos extremos.

Herencia más una interfaz

Lesson es abstracta; VideoLesson, Quiz y Assignment heredan su título y su posición. La calificación atraviesa ese árbol: los cuestionarios y las tareas se califican, los vídeos no, y una revisión entre pares añadida el año que viene podría calificarse sin ser una lección. Eso es una interfaz, Gradable, realizada por Quiz y Assignment. GradingService depende de Gradable, no de las clases concretas, y la flecha de dependencia discontinua dice exactamente eso.

Una asociación con datos propios

Enrollment se sitúa entre User y Course. UML tiene una notación específica para esto, la clase asociación, unida a la línea con una línea discontinua. La mayoría de los equipos la dibujan en cambio como una clase normal con dos asociaciones, porque tiene identidad, datos y comportamiento propios: la fecha de inscripción, el progreso, las entregas y como mucho un certificado.

Course se compone de Modules, que se componen de Lessons. VideoLesson, Quiz y Assignment heredan de la clase abstracta Lesson; Quiz y Assignment realizan la interfaz Gradable. Los usuarios imparten cursos y se inscriben mediante Enrollment, que guarda Submissions y un Certificate opcional.

32 componentes · 16 conexiones

Abrir en Archboard
Contenido del diagrama
  • Category
  • Course
  • User
  • Module
  • Enrollment
  • Certificate
  • Lesson
  • Submission
  • VideoLesson
  • Quiz
  • Assignment
  • Gradable
  • GradingService
  • groups
  • teaches
  • enrolls
  • for
  • earns
  • holds
  • answers
  • «use»
Un diagrama de clases UML para una plataforma de cursos en línea: cursos compuestos de módulos y lecciones, tres tipos de lección bajo una clase abstracta, los trabajos calificables detrás de una interfaz y las inscripciones que unen usuarios y cursos.

Cómo leerlo:

  • Arriba a la izquierda, el catálogo. Course se compone de Module, que se compone de Lesson, con rombos rellenos en el todo. Category agrupa cursos con un rombo hueco.
  • Abajo a la izquierda, los tipos de lección. Lesson es abstracta. VideoLesson, Quiz y Assignment apuntan a ella con triángulos huecos.
  • A la derecha, el aprendizaje. Un User imparte cursos, con 1..* instructores por curso, y tiene cualquier número de inscripciones, cada una para exactamente un curso. Un Enrollment guarda entregas y obtiene como mucho un Certificate.
  • Abajo a la derecha, la calificación. Quiz y Assignment realizan Gradable con líneas discontinuas y triángulos huecos. Cada Submission responde a un elemento calificable, y GradingService depende solo de la interfaz.

Errores comunes

  • Todas las clases del código. Entre diez y quince clases por diagrama es un buen límite. Divide un modelo más grande por áreas o muestra el resto como nombres sin compartimentos.
  • Getters y setters como operaciones. Enumera operaciones con significado de dominio, como publish() o enroll(), no los métodos de acceso.
  • Tablas dibujadas como clases. Atributos como courseId pertenecen al diagrama ER. En un diagrama de clases, la línea de asociación es la referencia.
  • Herencia para roles. Si un objeto puede ganar o perder el rol, es una asociación, no una subclase.
  • Rombos sin significado. Si nadie sabe decir qué le pasa a las partes al borrar el todo, usa una asociación simple.
  • Detalles de implementación a nivel de dominio. Deja fuera las anotaciones del framework, los DTO y los repositorios, salvo que el diagrama trate de ellos.

Del diagrama al código

El diagrama se traslada directamente al código. En 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;
  }
}

La composición se convierte en propiedad: el curso crea sus módulos, y nada mantiene vivo un módulo cuando el curso ha desaparecido. La agregación se convierte en una referencia a un objeto con su propio ciclo de vida. Cuando el código y el diagrama no coinciden, actualiza el diagrama en el mismo pull request; es mucho más fácil cuando el diagrama es un enlace y no una captura de pantalla, como se explica en diagramas de arquitectura que no se quedan obsoletos.

Constrúyelo en Archboard

  1. Inicia sesión con Google o GitHub y crea un diagrama.
  2. Del pack UML, añade una Clase por cada concepto y escribe atributos y operaciones, uno por línea. Pon el estereotipo de Lesson en abstract.
  3. Añade una Interfaz para Gradable con sus dos operaciones.
  4. Conecta las clases. Pon el extremo de flecha del lado del todo en Rombo relleno para la composición y en Rombo para la agregación, y en Triángulo del lado del padre para la herencia. Dibuja la realización como una línea discontinua con un Triángulo en la interfaz, y la dependencia como una línea discontinua con una Flecha abierta.
  5. Pon las multiplicidades como texto junto a cada extremo y nombra las asociaciones, como "teaches", en la etiqueta de la conexión.
  6. Añade una Nota UML para las decisiones, como por qué los roles son asociaciones.

El ejemplo usa 13 nodos de diagrama, dentro del plan Free. Con un agente de IA conectado por MCP puedes describir los requisitos y dejar que esboce las clases, y después corregir tú las multiplicidades.

Preguntas frecuentes

¿Qué diferencia hay entre un diagrama de clases y un diagrama de objetos? Un diagrama de clases muestra tipos y las reglas entre ellos. Un diagrama de objetos muestra instancias concretas en un momento dado, con sus valores y enlaces, y sirve para comprobar un diagrama de clases con un ejemplo real.

¿Qué diferencia hay entre agregación y composición? Con la composición, una parte pertenece a un único todo y se borra con él. Con la agregación, las partes pueden existir por su cuenta y compartirse; su significado exacto queda a criterio del modelador, así que muchos equipos usan en su lugar una asociación simple.

¿Debe un diagrama de clases coincidir exactamente con el código? Un modelo de dominio debe coincidir con los conceptos y las relaciones, no con cada campo y cada método. Mantenlo a un nivel que puedan leer tanto una product manager como un ingeniero.

¿En qué se diferencia un diagrama de clases de un diagrama de componentes C4? Un diagrama de componentes C4 muestra bloques desplegables y sus responsabilidades. Un diagrama de clases baja un nivel más, a los tipos que hay dentro; consulta el ejemplo de diagrama del modelo C4.

¿Puedo generar un diagrama de clases automáticamente a partir del código? Muchos IDE pueden, pero el resultado muestra todas las clases y cuesta leerlo. Dibuja el modelo de dominio a mano o con un agente de IA, y limítalo a las clases que importan.

Empieza gratis — un puesto, sin tarjeta de crédito.

Artículos relacionados

← Volver al blog

Abre un lienzo en un minuto

Inicia sesión con Google o GitHub. Gratis para un asiento. Mejora el plan cuando se una el equipo.