Sistema de gestión académica desarrollado en Java 17 con Maven, aplicando principios SOLID, POO, herencia, polimorfismo, colecciones genéricas, enumeraciones y excepciones personalizadas.
- Características
- Estructura del Proyecto
- Requisitos
- Instalación y Ejecución
- Uso
- Arquitectura y Patrones
- Modelo de Dominio
- Enumeraciones
- Excepciones Personalizadas
- Capa de Repositorio
- Capa de Servicios
- Principios SOLID Aplicados
- Tests
- Datos de Prueba
- Posibles Mejoras
- Gestión completa de estudiantes: CRUD, búsqueda por matrícula/DNI/nombre, filtrado por nivel y estado
- Gestión de asignaturas: CRUD, prerrequisitos, tipos (obligatoria/optativa), cursos y cuatrimestres
- Matrículas: Inscripción con validación de prerrequisitos, límite de créditos (máx 90), tipos de matrícula
- Notas y calificaciones: Registro con validación (0-10), cálculo de media ponderada por créditos
- Reportes y consultas: Por nivel, estado, tipo, curso, profesor, estudiantes con pendientes, graduables
- Estadísticas globales: Conteo por categorías, media global, avance hacia graduación
- CLI interactivo: Menús navegables con validación de entrada
- Datos de prueba: 3 estudiantes, 8 asignaturas, matrículas y notas precargadas
ModuloEstudiantes/
├── pom.xml
├── README.md
├── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── example/
│ │ ├── App.java # Entry point + CLI
│ │ ├── domain/ # Entidades de dominio
│ │ │ ├── Persona.java # Clase base abstracta
│ │ │ ├── Estudiante.java # Estudiante (extends Persona)
│ │ │ ├── Asignatura.java # Asignatura/Materia
│ │ │ ├── AsignaturaMatriculada.java # Composición: estudiante + asignatura + metadata
│ │ │ └── Profesor.java # Profesor (extends Persona)
│ │ ├── enums/ # Enumeraciones
│ │ │ ├── NivelCurso.java # PRIMARIA, SECUNDARIA, BACHILLERATO, FP, UNIVERSIDAD, MASTER, DOCTORADO
│ │ │ ├── EstadoEstudiante.java # ACTIVO, INACTIVO, GRADUADO, RETIRADO, SUSPENDIDO, EN_PROCESO
│ │ │ ├── TipoAsignatura.java # OBLIGATORIA, OPTATIVA, LIBRE_CONFIGURACION, PRACTICUM, TFG, TFM
│ │ │ └── TipoMatricula.java # NORMAL, REPETICION, RECONOCIMIENTO, CONVALIDACION, MOVILIDAD, EXTRAORDINARIA, ESPECIAL
│ │ ├── exceptions/ # Excepciones personalizadas (8)
│ │ │ ├── NotaInvalidaException
│ │ │ ├── EstudianteNoEncontradoException
│ │ │ ├── AsignaturaNoEncontradaException
│ │ │ ├── MatriculaException
│ │ │ ├── NumeroExpedienteInvalidoException
│ │ │ ├── AsignaturaYaMatriculadaException
│ │ │ ├── PrerrequisitosNoCumplidosException
│ │ │ └── CreditosInsuficientesException
│ │ ├── repository/ # Capa de acceso a datos (in-memory)
│ │ │ ├── EstudianteRepository.java # Interface
│ │ │ ├── EstudianteRepositoryImpl.java # Implementación ConcurrentHashMap
│ │ │ ├── AsignaturaRepository.java # Interface
│ │ │ └── AsignaturaRepositoryImpl.java # Implementación ConcurrentHashMap
│ │ └── service/ # Capa de lógica de negocio
│ │ ├── EstudianteService.java # Interface
│ │ ├── EstudianteServiceImpl.java
│ │ ├── AsignaturaService.java # Interface
│ │ ├── AsignaturaServiceImpl.java
│ │ ├── MatriculaService.java # Interface
│ │ └── MatriculaServiceImpl.java
│ └── test/
│ └── java/
│ └── com/
│ └── example/
│ └── AppTest.java # Test básico JUnit 5
- Java 17+ (configurado en
maven.compiler.release=17) - Maven 3.8+
- Sin dependencias externas de runtime (solo JUnit 5 para tests)
mvn compilemvn testmvn exec:java -Dexec.mainClass="com.example.App"mvn package
java -jar target/ModuloEstudiantes-1.0-SNAPSHOT.jarNota: Para JAR ejecutable añadir en
pom.xmldentro de<build><plugins>:<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <mainClass>com.example.App</mainClass> </manifest> </archive> </configuration> </plugin>
Al ejecutar la aplicación aparece el menú principal:
╔══════════════════════════════════════════════════════════════╗
║ SISTEMA DE GESTIÓN DE ESTUDIANTES ║
╠══════════════════════════════════════════════════════════════╣
║ 1. Gestión de Estudiantes ║
║ 2. Gestión de Asignaturas ║
║ 3. Gestión de Matrículas ║
║ 4. Gestión de Notas ║
║ 5. Consultas y Reportes ║
║ 6. Estadísticas ║
║ 0. Salir ║
╚══════════════════════════════════════════════════════════════╝
- Crear asignaturas (opción 2 → 1) con código, nombre, créditos, tipo, curso, cuatrimestre, profesor y prerrequisitos
- Crear estudiantes (opción 1 → 1) con datos personales, NIF, email, fecha nacimiento, número de expediente, nivel y fecha matrícula
- Matricular estudiantes (opción 3 → 1) seleccionando estudiante, asignatura, grupo, profesor, aula, horario y tipo de matrícula
- Registrar notas (opción 4 → 1) con calificación (0-10) y fecha de examen
- Consultar reportes (opción 5) o ver estadísticas (opción 6)
| Estudiante | Expediente | Nivel | Asignaturas matriculadas |
|---|---|---|---|
| Juan Pérez García | EST001 | UNIVERSIDAD | MAT101, FIS101, INF101 |
| María López Martín | EST002 | UNIVERSIDAD | MAT101, FIS101, INF101 |
| Carlos Ruiz Sánchez | EST003 | UNIVERSIDAD | MAT102, FIS102, INF102 |
Asignaturas: 8 (6 obligatorias 1º curso + 2 optativas 2º curso) con prerrequisitos configurados.
| Patrón | Dónde se aplica |
|---|---|
| Repository Pattern | EstudianteRepository, AsignaturaRepository - abstracción de persistencia |
| Service Layer | *Service / *ServiceImpl - lógica de negocio, validaciones, transacciones |
| Dependency Injection | Constructores de servicios aceptan interfaces de repositorio |
| Factory Method | crearEstudiante(), crearAsignatura() en servicios |
| Template Method | Persona.calcularEdad(), Estudiante.recalcularNotaMedia() |
| Strategy | TipoMatricula define comportamiento de matrícula |
┌─────────────────────────────────────────────────────────────┐
│ App.java (CLI) │
└──────────────────────────┬──────────────────────────────────┘
│ usa
┌──────────────────┼──────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│Estudiante │ │Asignatura │ │Matricula │
│Service │ │Service │ │Service │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│Estudiante │ │Asignatura │ │(usa ambos │
│Repository │ │Repository │ │ repositorios) │
└───────────────┘ └───────────────┘ └───────────────┘
- nombre, apellidos, dni, email, telefono, fechaNacimiento
+ getNombreCompleto()
+ calcularEdad()- numeroExpediente, nivelCurso, estado, fechaMatricula
- asignaturasMatriculadas: Map<String, AsignaturaMatriculada>
- calificaciones: Map<String, Double>
- fechasExamen: Map<String, LocalDate>
- asignaturasAprobadas: Set<String>
- tiposMatricula: Map<String, TipoMatricula>
- notaMedia, creditosSuperados, creditosMatriculados
- beca, observaciones
+ matricularAsignatura(AsignaturaMatriculada, TipoMatricula)
+ anularMatricula(String codigo)
+ registrarCalificacion(String, double, LocalDate)
+ getNotaMedia(), getCreditosSuperados(), getCreditosPendientes()
+ getPorcentajeAvance() // % hacia 240 créditos
+ puedeGraduarse() // 240 créditos + media >= 5.0- codigo, nombre, descripcion, creditos, tipo, curso, cuatrimestre, profesor
- prerrequisitos: Set<String>
- equivalencias: Set<String>
+ cumplePrerrequisitos(Set<String> aprobadas)
+ agregarPrerrequisito(String)- asignatura: Asignatura (final)
- grupo, profesor, fechaMatricula, aula, horario
- tipoMatricula: TipoMatricula- codigoProfesor, departamento, especialidad, nivelMaximoImpartir
- asignaturasImpartidas: Set<String>
- gruposAsignados: Map<String, List<String>>
- fechaContratacion, activo
+ getAntiguedad()| Valor | Descripción |
|---|---|
| PRIMARIA | Primaria |
| SECUNDARIA | Secundaria |
| BACHILLERATO | Bachillerato |
| FORMACION_PROFESIONAL | Formación Profesional |
| UNIVERSIDAD | Universidad |
| MASTER | Máster |
| DOCTORADO | Doctorado |
| Valor | Descripción |
|---|---|
| ACTIVO | Activo |
| INACTIVO | Inactivo |
| GRADUADO | Graduado |
| RETIRADO | Retirado |
| SUSPENDIDO | Suspendido |
| EN_PROCESO | En Proceso |
| Valor | Descripción |
|---|---|
| OBLIGATORIA | Obligatoria |
| OPTATIVA | Optativa |
| LIBRE_CONFIGURACION | Libre Configuración |
| PRACTICUM | Prácticum |
| TRABAJO_FIN_GRADO | Trabajo Fin de Grado |
| TRABAJO_FIN_MASTER | Trabajo Fin de Máster |
| Valor | Descripción |
|---|---|
| NORMAL | Normal |
| REPETICION | Repetición |
| RECONOCIMIENTO | Reconocimiento de créditos |
| CONVALIDACION | Convalidación |
| MOVILIDAD | Movilidad/Erasmus |
| EXTRAORDINARIA | Extraordinaria |
| ESPECIAL | Especial |
| Excepción | Cuándo se lanza |
|---|---|
NotaInvalidaException |
Nota fuera de rango 0-10 |
EstudianteNoEncontradoException |
Buscar/operar con expediente inexistente |
AsignaturaNoEncontradaException |
Buscar/operar con código inexistente |
MatriculaException |
Error genérico de matrícula |
NumeroExpedienteInvalidoException |
Expediente vacío/duplicado al crear |
AsignaturaYaMatriculadaException |
Intentar matricular duplicado |
PrerrequisitosNoCumplidosException |
No tiene asignaturas base aprobadas |
CreditosInsuficientesException |
Supera límite 90 créditos matriculados |
Todas extienden Exception (checked) para forzar manejo explícito.
// EstudianteRepository
void guardar(Estudiante)
Optional<Estudiante> buscarPorNumeroExpediente(String)
Optional<Estudiante> buscarPorDni(String)
List<Estudiante> buscarTodos()
List<Estudiante> buscarPorNivel(NivelCurso)
List<Estudiante> buscarPorEstado(EstadoEstudiante)
List<Estudiante> buscarPorNombre(String)
void eliminar(String)
boolean existe(String)
long contar()
// AsignaturaRepository
void guardar(Asignatura)
Optional<Asignatura> buscarPorCodigo(String)
List<Asignatura> buscarTodos()
List<Asignatura> buscarPorNombre(String)
List<Asignatura> buscarPorCurso(int)
List<Asignatura> buscarPorTipo(TipoAsignatura)
List<Asignatura> buscarPorCuatrimestre(int)
List<Asignatura> buscarPorProfesor(String)
void actualizar(Asignatura)
void eliminar(String)
boolean existe(String)
long contar()
long contarPorTipo(TipoAsignatura)
Set<String> obtenerTodosCodigos()- Thread-safe:
ConcurrentHashMap - Índices duales: Por expediente y por DNI (estudiantes)
- Streams API: Filtros funcionales para consultas
- Inmutabilidad: Devuelven copias/unmodifiable views
crearEstudiante(...)- Valida duplicados, crea y persistebuscarPorNumeroExpediente/ DNI / nombrelistarTodos() / porNivel() / porEstado()cambiarEstado() / cambiarNivel()eliminarEstudiante()contarEstudiantes() / porNivel() / porEstado()
crearAsignatura()- Valida código únicobuscarPorCodigo/ Nombre/ Tipo/ Curso/ Cuatrimestre/ ProfesoractualizarAsignatura() / eliminarAsignatura()contar() / contarPorTipo() / obtenerTodosCodigos()
matricularEstudiante()- Valida: existe estudiante/asignatura, no duplicado, prerrequisitos, límite créditosanularMatricula()- Recalcula créditosregistrarNota()- Valida matriculado, rango nota, actualiza media y créditos superadosobtenerAsignaturasMatriculadas()cambiarTipoMatricula()obtenerCreditosMatriculados() / Superados()
| Principio | Implementación |
|---|---|
| SRP (Single Responsibility) | Cada clase tiene una razón para cambiar: Estudiante (datos + reglas), EstudianteRepository (persistencia), EstudianteService (orquestación) |
| OCP (Open/Closed) | Nuevos TipoAsignaturas via enum TipoAsignatura; nuevos estados via EstadoEstudiante`; nuevos repositorios implementando interfaces sin tocar servicios |
| LSP (Liskov Substitution) | EstudianteRepositoryImpl sustituible por JpaEstudianteRepository futuro; lo mismo para servicios |
| ISP (Interface Segregation) | 3 interfaces de servicio separadas (EstudianteService, AsignaturaService, MatriculaService) en lugar de una monolítica |
| DIP (Dependency Inversion) | Servicios dependen de interfaces Repository, no de implementaciones. Inyección por constructor en *ServiceImpl |
mvn testEjecuta AppTest.java (1 test básico). Para ampliar:
// src/test/java/com/example/EstudianteServiceTest.java
@ExtendWith(MockitoExtension.class)
class EstudianteServiceTest {
@Mock EstudianteRepository repo;
@InjectMocks EstudianteServiceImpl service;
@Test void crearEstudiante_validaDuplicado() { ... }
@Test void matricular_verificaPrerrequisitos() { ... }
}Dependencias de test en pom.xml: JUnit Jupiter 5.11, Mockito (añadir si se desea).
Al iniciar App.inicializarDatosPrueba() se crean:
| Código | Nombre | Créditos | Tipo | Curso | Cuatri | Profesor | Prerreq |
|---|---|---|---|---|---|---|---|
| MAT101 | Matemáticas I | 6 | OBLIGATORIA | 1 | 1 | Dr. García | - |
| FIS101 | Física I | 6 | OBLIGATORIA | 1 | 1 | Dr. López | - |
| INF101 | Programación I | 6 | OBLIGATORIA | 1 | 1 | Dra. Martín | - |
| MAT102 | Matemáticas II | 6 | OBLIGATORIA | 1 | 2 | Dr. García | MAT101 |
| FIS102 | Física II | 6 | OBLIGATORIA | 1 | 2 | Dr. López | FIS101 |
| INF102 | Programación II | 6 | OBLIGATORIA | 1 | 2 | Dra. Martín | INF101 |
| OPT201 | Inteligencia Artificial | 6 | OPTATIVA | 2 | 1 | Dr. Ruiz | - |
| OPT202 | Bases de Datos | 6 | OPTATIVA | 2 | 1 | Dra. Sánchez | - |
| Expediente | Nombre | DNI | Nivel | Fecha Matrícula |
|---|---|---|---|---|
| EST001 | Juan Pérez García | 12345678A | UNIVERSIDAD | 2023-09-01 |
| EST002 | María López Martín | 87654321B | UNIVERSIDAD | 2023-09-01 |
| EST003 | Carlos Ruiz Sánchez | 11223344C | UNIVERSIDAD | 2022-09-01 |
- EST001: MAT101(8.5), FIS101(7.0), INF101(9.0) → Media ~8.17, 18 créditos
- EST002: MAT101(6.5), FIS101(5.5), INF101(7.5) → Media ~6.5, 18 créditos
- EST003: MAT102(8.0), FIS102(7.5), INF102(9.5) → Media ~8.33, 18 créditos
- JPA/Hibernate + PostgreSQL/H2
- Migración Flyway/Liquibase
- Repositorios JPA implementando interfaces actuales
- Spring Boot / Quarkus / Micronaut
- DTOs + MapStruct
- OpenAPI/Swagger
- Validación Bean Validation (@Valid)
- Spring Security / JWT
- Roles: ADMIN, SECRETARIA, PROFESOR, ESTUDIANTE
- Auditoría (createdBy, updatedAt)
- Actas de examen
- Expediente académico PDF
- Prerrequisitos complejos (OR/AND)
- Planes de estudios por titulación
- Movilidad/Convalidaciones automáticas
- Notificaciones email
- Importación/Exportación CSV/Excel
- Tests unitarios (Mockito) >80% cobertura
- Tests de integración (Testcontainers)
- Tests de contrato (Pact)
- Mutation testing (Pitest)
- Web: Thymeleaf / Vaadin / React
- Desktop: JavaFX
- Mobile: Capacitor/Ionic
- Dockerfile + docker-compose
- CI/CD GitHub Actions / GitLab CI
- SonarQube quality gate
- Observabilidad: Micrometer + Prometheus + Grafana
Proyecto educativo - Libre uso y modificación.
Desarrollado como ejemplo de arquitectura Java empresarial aplicando principios SOLID, clean architecture y patrones de diseño clásicos.