1Generar el esqueleto con Spring Initializr

start.spring.io genera el proyecto Maven con la estructura base.

CampoValor
ProjectMaven
LanguageJava
Spring Boot4.x (última estable)
Groupes.edu.multagal
Artifactmultagal
Java25
PackagingJar
DependenciasSpring Web, Spring Boot DevTools, Lombok (opcional), Validation
bash
curl https://start.spring.io/starter.zip \
  -d type=maven-project -d language=java -d bootVersion=3.4.1 \
  -d groupId=es.edu.multagal -d artifactId=multagal \
  -d name=multagal -d packageName=es.edu.multagal \
  -d javaVersion=25 -d dependencies=web,devtools,lombok,validation \
  -o multagal.zip
unzip multagal.zip -d multagal

Estructura de paquetes obligatoria dentro de es.edu.multagal, con dependencias que fluyen en un solo sentido (controller nunca llama a repository; domain no depende de nada):

text
es.edu.multagal/
├── MultagalApplication.java
├── config/           ← @Configuration y @ConfigurationProperties
├── domain/
│   ├── model/        ← entidades, records, enums, VOs
│   └── exception/    ← jerarquía MultagalException
├── repository/       ← interfaces + implementaciones de acceso a datos
├── service/          ← lógica de negocio
├── controller/       ← puntos de entrada HTTP
└── dto/              ← objetos de transferencia

2compose.yaml y variables de entorno

Tres gestores de bases de datos (MariaDB para UD3, PostgreSQL+PostGIS para UD5, MongoDB para UD6) levantados con un fichero y un comando.

yaml
name: multagal

services:
  # ─── UD3: MariaDB ──────────────────────
  mariadb:
    image: mariadb:11
    container_name: multagal-mariadb
    restart: unless-stopped
    environment:
      MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
      MARIADB_DATABASE: multagal
      MARIADB_USER: ${MARIADB_USER}
      MARIADB_PASSWORD: ${MARIADB_PASSWORD}
    ports:
      - "3306:3306"
    volumes:
      - mariadb-data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 10s
      timeout: 5s
      retries: 5

  # ─── UD5: PostgreSQL + PostGIS ─────────
  postgres:
    image: postgis/postgis:16-3.4
    container_name: multagal-postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: multagal
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    ports:
      - "5432:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d multagal"]
      interval: 10s
      timeout: 5s
      retries: 5

  # ─── UD6: MongoDB ──────────────────────
  mongo:
    image: mongo:7
    container_name: multagal-mongo
    restart: unless-stopped
    environment:
      MONGO_INITDB_ROOT_USERNAME: ${MONGO_USER}
      MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASSWORD}
      MONGO_INITDB_DATABASE: multagal
    ports:
      - "27017:27017"
    volumes:
      - mongo-data:/data/db
    healthcheck:
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  mariadb-data:
  postgres-data:
  mongo-data:
PropiedadPropósito
imageVersión fija (nunca latest, para reproducibilidad)
ports"host:contenedor" — permite conexión externa
volumesPersistencia de datos crítica
healthcheckVerifica que el servicio responde realmente
$$ en healthcheckEscapa la interpolación de Compose (variable local del contenedor)
text
# .env (NO se sube al repositorio)
MARIADB_ROOT_PASSWORD=cambiaEstaClaveRoot
MARIADB_USER=multagal
MARIADB_PASSWORD=cambiaEstaClave
POSTGRES_USER=multagal
POSTGRES_PASSWORD=cambiaEstaClave
MONGO_USER=multagal
MONGO_PASSWORD=cambiaEstaClave

# .env.example (SÍ se sube, con valores vacíos)
MARIADB_ROOT_PASSWORD=
MARIADB_USER=multagal
MARIADB_PASSWORD=
POSTGRES_USER=multagal
POSTGRES_PASSWORD=
MONGO_USER=multagal
MONGO_PASSWORD=
Nunca subas .envAñádelo a .gitignore antes del primer commit. Las contraseñas commiteadas penalizan y no se eliminan del historial aunque se borren después.

3application.yml y perfiles

Un perfil por entorno permite ejecutar el mismo código en desarrollo y producción.

yaml
# application.yml
spring:
  application:
    name: multagal
  profiles:
    default: dev
server:
  port: 8080
logging:
  level:
    es.edu.multagal: DEBUG

# application-dev.yml
spring:
  datasource:
    url: jdbc:h2:mem:multagal;DB_CLOSE_DELAY=-1
    username: sa
    password:
  h2:
    console:
      enabled: true

# application-prod.yml
spring:
  datasource:
    url: jdbc:mariadb://localhost:3306/multagal
    username: ${MARIADB_USER}
    password: ${MARIADB_PASSWORD}
logging:
  level:
    root: WARN
Orden de precedenciaArgumentos CLI (--server.port=9090) > variables de entorno (SERVER_PORT=9090) > application-{perfil}.yml > application.yml > valores por defecto en código.

4Primer arranque y verificación

@SpringBootApplication combina @SpringBootConfiguration + @ComponentScan + @EnableAutoConfiguration: el Condition Evaluation Report muestra qué autoconfiguraciones se activan según lo que hay en el classpath.

bash
./mvnw spring-boot:run
docker compose up -d
docker compose ps
./mvnw spring-boot:run -Dspring-boot.run.arguments=--debug
Dónde mirarEn la salida de --debug, busca la sección Positive matches (autoconfiguraciones activadas) y Negative matches (desactivadas, con la condición que faltó). Necesitas localizar tres activadas y una desactivada con su justificación.

5Entregable: ADR de autoconfiguración

Formato ADR simplificado en docs/adr/, registrando decisión, alternativas descartadas y consecuencias.

text
## ADR-000 — [Nombre de la autoconfiguración]
**Fecha:** AAAA-MM-DD
**Estado:** aceptada

### Contexto
¿Qué condición del classpath o de la configuración la activa o desactiva?

### Decisión
[Activada / Desactivada] porque...

### Consecuencias
...

!Problemas frecuentes

"Port 8080 was already in use"Para la instancia en ejecución o usa --server.port=8081.
"port is already allocated" (Docker)Un servicio nativo ya usa ese puerto. Cambia el puerto en compose.yaml (por ejemplo "3307:3306") o para el servicio nativo.
Cambios en .env sin efectoLas imágenes aplican las variables solo en el primer arranque. Solución: docker compose down -v && docker compose up -d (borra los datos).
Consumo excesivo de memoria en WindowsDocker Desktop en WSL 2 acapara RAM. Crea C:\Users\<usuario>\.wslconfig con [wsl2], memory=6GB, processors=4, y ejecuta wsl --shutdown.

✓Criterios de aceptación

  • ./mvnw clean package termina sin errores
  • La aplicación arranca registrando el perfil dev activo
  • docker compose ps muestra los tres servicios healthy
  • Sin contraseñas en compose.yaml ni en el código
  • Informe de autoconfiguración correctamente interpretado

Adaptado de apartado d — Docker y servicios de datos y apartado e — Anatomía de un proyecto Spring Boot.