Actividades de la UD0
Seis actividades, 7 horas. Al terminarlas debes tener el entorno verificado, el proyecto arrancando, el dominio esbozado, una vertical mínima probada y expuesta por HTTP, y el repositorio publicado con su primera etiqueta.
El conjunto de la UD0 forma parte de la ENTREGA 1 y se materializa como una
rama hito/ud0-arranque fusionada en main. No evalúa ningún RA directamente,
pero sin ella no se puede abordar ninguna unidad posterior.
A0.1 — Instalación y verificación del entorno
Objetivo: disponer de un entorno de desarrollo completo y reproducible.
Tareas
- Instala JDK 25, Maven, Git, Docker y tu IDE, siguiendo el apartado c.
- Configura
user.nameyuser.emailen Git con tu nombre real. - Crea la cuenta en GitHub, GitLab o Bitbucket y registra tu clave SSH.
- Ejecuta la checklist de arranque completa y guarda la salida.
Entregable
Un fichero docs/entorno.md en el repositorio con la salida literal de:
java -version
mvn -version
git --version
docker --version
docker compose version
Criterios de aceptación
| # | Criterio |
|---|---|
| 1 | java -version responde 25.x |
| 2 | mvn -version confirma que usa el JDK 25 |
| 3 | todos los comandos han encontrado versiones validas |
A0.2 — Generación del esqueleto y primer arranque
Objetivo: comprender la estructura de un proyecto Spring Boot.
Tareas
- Genera el proyecto con Spring Initializr según la configuración del apartado e.
- Crea la estructura de paquetes de la
arquitectura en capas:
config,domain.model,domain.exception,repository,service,controller,dto. - Crea el
compose.yamlcon los tres servicios de datos, más.envy.env.example. - Configura
application.ymly los perfilesdevyprod. - Arranca la aplicación y comprueba que Tomcat responde en el 8080.
- Levanta los tres contenedores y verifica que están
healthy. - Ejecuta
./mvnw spring-boot:run -Dspring-boot.run.arguments=--debugy localiza en el Condition Evaluation Report tres autoconfiguraciones que se hayan activado y una que no, con su motivo.
Entregable
- Proyecto que arranca con
./mvnw spring-boot:run. compose.yamlfuncional.- En
docs/adr/0000-autoconfiguracion.md: las cuatro autoconfiguraciones localizadas, con la explicación de por qué se activaron o no.
Criterios de aceptación
| # | Criterio |
|---|---|
| 1 | ./mvnw clean package termina sin errores |
| 2 | La aplicación arranca y registra el perfil dev activo |
| 3 | docker compose ps muestra los tres servicios healthy |
| 4 | No hay ninguna contraseña escrita en compose.yaml ni en el código |
| 5 | El informe de autoconfiguración está correctamente interpretado |
A0.3 — Modelos de dominio de MULTAGAL
Objetivo: traducir el modelo conceptual a tipos Java.
Tareas
Crea en domain/model los tipos que representan el dominio descrito en el
apartado b. Todavía sin persistencia: son
POJOs o record, sin anotaciones de JPA ni de Jackson.
Como mínimo:
Conductor,PermisoConduccion,Vehiculo,TitularidadInfraccion,Denuncia,ExpedienteNotificacion,Alegacion,Resolucion,Pago,Radar- Los enumerados:
Gravedad,EstadoExpediente,EstadoPermiso,ResultadoNotificacion - Los objetos valor:
Coordenadas,DireccionPostal,Matricula
Decisiones que debes tomar y justificar
recordo clase. ¿Cuáles de estos tipos son inmutables por naturaleza y cuáles necesitarán mutabilidad más adelante? Ten en cuenta que en la UD4 JPA exige constructor sin argumentos y campos mutables: ¿afecta eso a la decisión de ahora?- Tipos de los importes.
BigDecimal, nuncadouble. Explica por qué en el ADR. - Tipos de las fechas.
LocalDatefrente aLocalDateTime: ¿cuál para la fecha de comisión de la infracción? ¿Y para el plazo de pronto pago? - Objetos valor frente a
String. ¿Matriculadebe ser un tipo propio o basta con unString? Argumenta. - Nulos. ¿Qué campos pueden ser
nulllegítimamente? Recuerda queDenuncia.conductorlo es cuando la capta un radar.
Entregable
- Los tipos del dominio en
domain/model. docs/adr/0001-modelado-del-dominio.mdcon las cinco decisiones anteriores, sus alternativas descartadas y el porqué.
Criterios de aceptación
| # | Criterio |
|---|---|
| 1 | Están modeladas las doce entidades del dominio |
| 2 | Los importes usan BigDecimal y las fechas la API java.time |
| 3 | Los enumerados sustituyen a los String mágicos |
| 4 | Ningún tipo del paquete domain importa nada de Spring |
| 5 | El ADR justifica las decisiones, no sólo las describe |
[Sobre el criterio 4]
Es la regla de dependencia del apartado
f. Si tu
Expediente importa org.springframework.*, el dominio ha dejado de ser el
núcleo estable. Compruébalo mirando los import.
A0.4 — Primer servicio inyectado y su prueba unitaria
Objetivo: aplicar inyección de dependencias y probar sin infraestructura.
Tareas
- Define la interfaz
RepositorioExpedientesenrepository, con al menos:guardar,buscarPorNumero,buscarTodosybuscarPorEstado. - Implementa
RepositorioExpedientesMemoriacon unConcurrentHashMap, anotada con@Repository. Es una implementación provisional: en la UD3 la sustituirá una JDBC sin tocar la capa de servicio. - Crea
ServicioExpedientesenservice, con inyección por constructor, que ofrezca:obtener(String numero)— lanzaExpedienteNoEncontradoExceptionsi no existe.registrar(Expediente e)— rechaza un número duplicado.calcularImporteConBonificacion(Expediente e, LocalDate fechaPago)— aplica la regla R1: 50 % si el pago se produce dentro de los 20 días naturales siguientes a la notificación.
- Crea la jerarquía inicial de excepciones en
domain/exception:MultagalExceptioncomo raíz, yExpedienteNoEncontradoExceptioncolgando de ella. - Escribe las pruebas unitarias de
ServicioExpedientescon Mockito, sin contexto de Spring y sin base de datos. Cubre:- El caso favorable de cada método.
- El expediente inexistente.
- El número duplicado.
- La bonificación en el día 20 y en el día 21 (los límites).
Entregable
Código y pruebas, con ./mvnw test en verde.
Criterios de aceptación
| # | Criterio |
|---|---|
| 1 | La inyección es por constructor y los campos son final |
| 2 | El servicio depende de la interfaz, no de la implementación |
| 3 | Las pruebas se ejecutan sin Docker y sin @SpringBootTest |
| 4 | Están cubiertos los casos límite del plazo de bonificación |
| 5 | El servicio no tiene estado mutable |
[Los límites importan]
«Dentro de los 20 días naturales siguientes» — ¿el día 20 entra o no entra? Esa ambigüedad del enunciado la tienes que resolver tú, documentarla y probarla. Los errores por uno son la fuente más común de defectos en cálculos de plazos, y en un procedimiento sancionador real tienen consecuencias jurídicas.
A0.5 — Controladores REST para el servicio de expedientes
Objetivo: exponer por HTTP el servicio construido en A0.4, con validación de entrada por anotaciones.
Tareas
- Crea en
dtolos DTOs de entrada y salida necesarios para el recursoexpedientes: como mínimoNuevoExpedienteRequest(o equivalente) yExpedienteResponse. Sigue el ejemplo del apartado g. - Anota los campos del DTO de entrada con las restricciones de Bean Validation que correspondan a cada campo (número obligatorio, importe positivo, formato de matrícula...).
- Crea
ExpedientesControllerencontroller, con inyección por constructor deServicioExpedientes, y expón como mínimo:POST /api/expedientes— crea un expediente a partir del DTO validado con@Valid. Responde201 Created.GET /api/expedientes/{numero}— devuelve el expediente o dispara la excepción de «no encontrado».
- Crea un
@RestControllerAdviceque traduzca:- los errores de validación del DTO a
400 Bad Requestcon el detalle de cada campo, siguiendo el ejemplo del apartado g; ExpedienteNoEncontradoExceptiona404 Not Found.
- los errores de validación del DTO a
- Comprueba manualmente con
curlo el cliente REST de tu IDE:- una petición válida (
201); - una petición con el número en blanco y el importe negativo (
400, con los dos campos en el cuerpo del error); - una consulta de un expediente inexistente (
404).
- una petición válida (
Entregable
Código del controlador, los DTOs y el manejador de errores, con la aplicación arrancando y respondiendo a las tres comprobaciones del punto 5.
Criterios de aceptación
| # | Criterio |
|---|---|
| 1 | El controlador depende del servicio por interfaz de service, nunca del repositorio directamente |
| 2 | El DTO de entrada usa Bean Validation y el método del controlador lleva @Valid |
| 3 | Una petición inválida responde 400 con el detalle de qué campo falló y por qué |
| 4 | Un expediente inexistente responde 404, no 500 ni 200 con cuerpo vacío |
| 5 | El DTO de entrada no valida nada que dependa del repositorio (duplicados, existencia) |
[Sobre el criterio 5]
El número de expediente duplicado ya lo rechaza ServicioExpedientes desde
la A0.4: esa comprobación no se repite en el DTO. Un DTO sólo valida la
forma del dato; que ya exista es lógica de negocio y vive en el service.
Repasa la distinción en el apartado
g
si tienes dudas.
A0.6 — Publicación del repositorio y configuración de ramas
Objetivo: establecer el flujo de trabajo profesional del curso.
Tareas
- Crea el
.gitignorey el.gitattributesdel apartado h antes del primer commit. - Inicializa el repositorio, crea el remoto privado y añade al profesorado como colaborador.
- Redacta un
README.mdcon: descripción del proyecto, requisitos previos, cómo levantar los servicios, cómo arrancar la aplicación y cómo ejecutar las pruebas. - Reorganiza el trabajo de A0.2–A0.5 en commits con sentido, siguiendo Conventional Commits. No vale un único commit con todo.
- Trabaja en la rama
hito/ud0-arranque, abre un pull request haciamain, autorrevísalo leyendo el diff completo y fusiónalo con--no-ff. - Etiqueta el resultado como
ud0-completadoy súbela.
Entregable
Repositorio publicado, con historial, PR fusionado y etiqueta.
Criterios de aceptación
| # | Criterio |
|---|---|
| 1 | El repositorio no contiene target/, .idea/ ni .env |
| 2 | El historial tiene al menos 8 commits con mensajes convencionales |
| 3 | Cada commit compila y las pruebas pasan |
| 4 | El PR está fusionado con --no-ff y la etiqueta ud0-completado apunta a main |
| 5 | El README.md permite arrancar el proyecto a alguien que lo clona por primera vez |
| 6 | docs/adr/ contiene al menos dos decisiones registradas |
[Prueba el criterio 5 de verdad]
Clona tu propio repositorio en otra carpeta y arráncalo siguiendo sólo lo que dice tu README, sin usar nada que sepas de memoria. Si te falta un paso, tu README está incompleto — y es exactamente lo que le pasará a quien lo corrija.
Autoevaluación de la unidad
Antes de dar la UD0 por cerrada, comprueba que sabes responder a esto sin consultar los apuntes. Son el tipo de preguntas de la defensa oral.
- ¿Qué diferencia hay entre una imagen y un contenedor? ¿Y entre un contenedor y una máquina virtual?
- ¿Por qué
docker compose down -ves peligroso? - ¿Qué hace exactamente
@SpringBootApplication? Nombra sus tres anotaciones. - Tienes un
@Serviceen el paquetees.otro.servicioy la aplicación enes.edu.multagal. ¿Qué ocurre al arrancar y por qué? - ¿Por qué se prefiere la inyección por constructor a la inyección por campo? Da dos razones.
- ¿Qué añade
@Repositoryque no añada@Component? - ¿Por qué un
@Serviceno debe tener campos mutables? - Explica el orden de precedencia de la configuración en Spring Boot.
- ¿Qué es un starter y qué relación tiene con la autoconfiguración?
- Enumera las reglas de dependencia entre las capas del proyecto. ¿Puede
controllerllamar arepository? - ¿Por qué el paquete
domainno debe importar nada de Spring? - ¿Por qué los importes van en
BigDecimaly no endouble? - ¿Qué evidencia aporta el historial de commits a la evaluación?
- ¿Qué diferencia hay entre una etiqueta y una rama?
- ¿Qué diferencia hay entre validar un DTO con
@Validy validar una regla de negocio como «el DNI ya está registrado»? ¿Por qué la segunda no puede resolverse en el DTO? - ¿Qué ocurre si un método de controlador recibe
@RequestBodysin@Valid?
Ampliación (opcional)
Para quien avance más rápido. No es exigible, pero se valora en el nivel de sobresaliente:
- Actuator. Añade
spring-boot-starter-actuatory explora/actuator/health,/actuator/beansy/actuator/configprops./actuator/beanste muestra el contenido real delApplicationContext: es la mejor forma de ver la inyección de dependencias. - Integración continua. Configura un workflow de GitHub Actions que ejecute
./mvnw testen cada push. Que el PR no se pueda fusionar con las pruebas en rojo. - Generador de datos sintéticos. Un componente que produzca conductores, vehículos y denuncias verosímiles y seudonimizados. Lo agradecerás en la UD3, cuando necesites volumen para que las consultas sean interesantes.
- Diagrama del dominio. Elabora el diagrama entidad-relación completo de MULTAGAL e inclúyelo en el README. Te servirá de guía durante todo el curso.