Un recorrido amable y progresivo para alguien que nunca escuchó hablar de Spring Boot. Primero entendemos qué es una aplicación web. Después vemos por qué Spring Boot ayuda. Recién ahí escribimos código.
Spring Boot es una forma ordenada de crear aplicaciones Java que reciben pedidos, aplican reglas y responden.
Por ejemplo: una app que recibe un ticket de venta, valida el total, lo guarda en una base de datos y responde si el pago fue aceptado.
No necesitás entender todo Spring el primer día. Al principio alcanza con esta idea:
Java es el lenguaje. Spring es una caja de herramientas. Spring Boot es el taller ya armado: banco de trabajo, enchufes, luces, herramientas básicas y reglas de orden.
Cuando escribís @RestController, no estás invocando magia. Estás diciendo: “esta clase recibe pedidos HTTP”. El curso va a explicar cada anotación desde el problema que resuelve.
El pedido entra como HTTP. Spring Boot lo convierte en un objeto Java.
La respuesta vuelve ordenada: status HTTP + JSON claro.
Node/Express puede ser una excelente opción para APIs livianas. El punto del ejemplo no es decir “Node está mal”. El punto es mostrar qué mejora Spring Boot cuando el proyecto empieza a necesitar reglas, validación, base de datos, tests y operación.
// index.js app.post('/tickets', async (req, res) => { const ticket = req.body; if (!ticket.terminal || ticket.total <= 0) { return res.status(400).json({ error: 'invalid ticket' }); } const saved = await db.insert(ticket); res.status(201).json(saved); });
Si el equipo no define estructura, es fácil que routing, validación, SQL, reglas y errores terminen mezclados en pocos archivos.
@RestController @RequestMapping("/tickets") class TicketController { private final TicketService service; @PostMapping ResponseEntity<TicketResponse> create( @Valid @RequestBody TicketRequest req) { return ResponseEntity.status(201) .body(service.create(req)); } }
El controller recibe HTTP. El service decide reglas. El repository guarda datos. La validación y los errores tienen lugar propio.
| Necesidad real | En una API rápida sin estructura | Con Spring Boot bien usado |
|---|---|---|
| Validar datos | Checks manuales repartidos. | @Valid, DTOs y mensajes de error consistentes. |
| Reglas de negocio | Mezcladas con rutas HTTP. | TicketService concentra decisiones. |
| Persistencia | SQL o driver usado desde cualquier lugar. | Repository separa acceso a datos. |
| Configuración | Variables y valores dispersos. | application.yml, profiles y propiedades externas. |
| Operación | Hay que armar health checks y métricas aparte. | Actuator expone salud y métricas de base. |
| Testing | Depende mucho de disciplina del equipo. | JUnit, MockMvc y slices de test ya integrados al ecosistema. |
La idea es construir confianza: primero entendés qué hace cada pieza; después la usás. HTTP, Controller, Service, Repository, Bean, configuración, test y deploy van apareciendo en ese orden.
Usá las flechas del teclado o los botones abajo. Cada módulo tiene un entregable. Al final tenés anexos de instalación, herramientas y prompts listos para copiar.
Funciona para probar. Escala mal cuando aparecen errores, validaciones, tests y configuración.
La arquitectura queda explícita. Spring crea objetos, conecta dependencias y levanta el servidor.
Setup
Instalar JDK, Maven, IDE, Git y crear proyecto.
HTTP
Entender request, response, JSON y status codes.
Controller
Primer endpoint real con Spring Web.
DI
Beans, services e inyección por constructor.
Datos
Repository, memoria, JDBC/H2 y SQL.
Calidad
Validación, errores, tests, Actuator, profiles.
Antes de Spring Boot, necesitás herramientas básicas. No muchas. Bien instaladas.
| Herramienta | Para qué sirve | Chequeo |
|---|---|---|
| JDK 21 | Compila y ejecuta Java. No alcanza con JRE. | java -version |
| Maven | Descarga dependencias, compila, ejecuta tests y empaqueta. | mvn -version |
| IDE | Editor con autocompletado, navegación y debugging. | Abrir proyecto sin errores. |
| Git | Versionado. Permite volver atrás cuando rompés algo. | git --version |
| curl/Postman | Probar endpoints HTTP sin depender del frontend. | curl --version |
Para aprender: Java 21 LTS + Maven + Spring Initializr + IntelliJ Community o VS Code. No arranques con Docker, Kubernetes, microservicios ni native-image.
com.exampleticket-api# Dependencias para empezar Spring Web Validation Spring Boot Actuator H2 Database # para aprender sin instalar DB Spring Data JDBC # opcional, se usa luego
Para el primer día, solo necesitás Spring Web. El resto entra cuando el concepto ya existe.
mvn spring-boot:run.Spring Boot recibe HTTP. Si HTTP no está claro, Spring parece magia.
| Pieza | Significado | Ejemplo |
|---|---|---|
| Método | Qué intención tiene el request. | GET, POST |
| Path | Qué recurso se quiere operar. | /tickets/1 |
| Body | Datos enviados al servidor. | JSON de alta de ticket |
| Status | Resultado técnico. | 200, 201, 400, 404, 500 |
| Verbo | Uso | Ejemplo |
|---|---|---|
| GET | Leer | GET /tickets/1 |
| POST | Crear o ejecutar acción | POST /tickets |
| PUT | Reemplazar | PUT /tickets/1 |
| PATCH | Cambiar parte | PATCH /tickets/1/status |
| DELETE | Borrar | DELETE /tickets/1 |
Usar GET para modificar datos: GET /payTicket?id=1. Puede funcionar, pero conceptualmente está mal. Pagar es una acción: usá POST.
El controller es la frontera HTTP de tu aplicación.
package com.example.ticketapi; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/hello") public String hello() { return "Spring Boot funciona"; } }
$ mvn spring-boot:run $ curl http://localhost:8080/hello
@RestController expone métodos como endpoints. @GetMapping conecta una URL con un método Java.
public record TicketResponse( Long id, String status, java.math.BigDecimal total ) {} @GetMapping("/tickets/{id}") public TicketResponse getTicket(@PathVariable Long id) { return new TicketResponse(id, "OPEN", new BigDecimal("123.45")); }
{
"id": 1,
"status": "OPEN",
"total": 123.45
}
DTO significa Data Transfer Object. Es el objeto que cruza la frontera de la API. No expongas entidades internas sin pensar.
GET /hello y GET /tickets/{id} funcionando.El corazón de Spring no es REST. Es el contenedor que crea y conecta objetos.
var repo = new TicketRepository(); var service = new TicketService(repo); var controller = new TicketController(service);
Vos decidís cuándo y cómo se crea cada objeto.
@Service public class TicketService { private final TicketRepository repo; public TicketService(TicketRepository repo) { this.repo = repo; } }
Spring crea el objeto y le pasa sus dependencias.
| Capa | Responsabilidad | No debería |
|---|---|---|
| Controller | HTTP, DTOs, status codes | Contener reglas de negocio complejas |
| Service | Reglas, casos de uso, transacciones | Saber detalles de HTTP |
| Repository | Persistencia | Decidir reglas del negocio |
@Service public class TicketService { private final TicketRepository repository; public TicketService(TicketRepository repository) { this.repository = repository; } public Ticket create(TicketRequest request) { if (request.total().signum() <= 0) { throw new IllegalArgumentException("total must be positive"); } return repository.save(Ticket.open(request.total())); } }
Primero memoria. Después SQL. No metas JPA hasta entender el flujo.
@Repository public class InMemoryTicketRepository implements TicketRepository { private final Map<Long, Ticket> data = new ConcurrentHashMap<>(); private final AtomicLong ids = new AtomicLong(); public Ticket save(Ticket ticket) { Long id = ids.incrementAndGet(); Ticket saved = ticket.withId(id); data.put(id, saved); return saved; } }
Separás el aprendizaje: primero Controller + Service + Repository. Después agregás SQL. Si todo entra junto, no sabés qué falló.
create table tickets (
id bigint generated by default as identity primary key,
status varchar(20) not null,
total decimal(18,2) not null,
created_at timestamp not null
);
spring.datasource.url=jdbc:h2:mem:ticketdb spring.h2.console.enabled=true spring.sql.init.mode=always
H2 sirve para aprender. Para producción elegís PostgreSQL, SQL Server, MySQL, etc.
Una API seria devuelve errores claros, no stack traces ni mensajes improvisados.
public record CreateTicketRequest( @NotNull @DecimalMin("0.01") BigDecimal total, @NotBlank String terminal ) {} @PostMapping("/tickets") public TicketResponse create( @Valid @RequestBody CreateTicketRequest request) { return service.create(request); }
La API no puede confiar en el cliente. Cualquier campo puede venir vacío, negativo, mal formado o directamente ausente.
No repitas validaciones manuales en cada controller. Usá Bean Validation para reglas estructurales.
@RestControllerAdvice public class ApiExceptionHandler { @ExceptionHandler(TicketNotFoundException.class) public ResponseEntity<ApiError> notFound( TicketNotFoundException ex) { return ResponseEntity.status(404) .body(new ApiError("TICKET_NOT_FOUND", ex.getMessage())); } }
| 200 | OK: lectura exitosa |
| 201 | Created: recurso creado |
| 400 | Bad Request: datos inválidos |
| 404 | Not Found: recurso inexistente |
| 409 | Conflict: regla de negocio bloquea operación |
El test no es burocracia. Es la forma de comprobar que entendiste.
| Tipo | Qué prueba | Herramienta |
|---|---|---|
| Unitario | Reglas del service sin levantar Spring. | JUnit + AssertJ |
| Web slice | Controller, JSON, status codes. | @WebMvcTest + MockMvc |
| Integración | App completa con contexto Spring. | @SpringBootTest |
| Persistencia | Repository y SQL. | H2/Testcontainers según madurez |
@Test void rejectsNegativeTotal() { TicketRepository repo = new InMemoryTicketRepository(); TicketService service = new TicketService(repo); CreateTicketRequest req = new CreateTicketRequest( new BigDecimal("-1.00"), "POS-1"); assertThatThrownBy(() -> service.create(req)) .isInstanceOf(IllegalArgumentException.class); }
Este test no necesita HTTP, JSON, Spring ni base. Prueba una regla. Cuanto más chico el test, más claro el fallo.
No alcanza con que compile. Tiene que arrancar, configurarse, loguear y exponer salud.
$ curl http://localhost:8080/actuator/health { "status": "UP" }
Sirve para saber si la app está viva y preparada para monitoreo.
# application-dev.properties server.port=8080 # application-prod.properties server.port=8085 logging.file.name=logs/ticket-api.log
Permite cambiar config sin tocar código.
mvn clean test
mvn clean package
java -jar target/ticket-api-0.0.1-SNAPSHOT.jar
java -jar target/ticket-api-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod
Checklist Windows pensado para alguien que arranca desde cero.
java -version y javac -version.JAVA_HOME apuntando al directorio del JDK, no a bin.%JAVA_HOME%\bin al PATH.mvnw.cmd que trae el proyecto generado.git --version.| Síntoma | Causa probable | Chequeo |
|---|---|---|
java no se reconoce | PATH mal configurado | Reabrir terminal y correr where java |
| Maven usa otro Java | JAVA_HOME apunta a JDK viejo | mvn -version |
| Puerto 8080 ocupado | Otra app está usando el puerto | Cambiar server.port=8081 |
| Dependencias no bajan | Proxy/firewall/red | Probar otra red o configurar proxy Maven |
| IDE marca todo rojo | No importó Maven | Reload Maven Project |
Qué hace cada herramienta en el curso y cuándo usarla.
Leer código, navegar clases, autocompletar, debuggear. Usalo para entender estructura.
Ejecutar comandos reproducibles: test, build, run, curl. Lo que funciona acá funciona en CI.
Administra dependencias y lifecycle: compile, test, package. No copies JARs manualmente.
Probar la API sin frontend. Es el tester mínimo de todo backend.
Guardar avances por checkpoint. Commit chico después de cada módulo funcionando.
Explicar errores, revisar código, crear ejercicios y generar tests. No reemplaza entender.
Prompts concretos para que el alumno no se quede trabado.
Estoy aprendiendo Spring Boot desde cero. Explicame qué problema resuelve [CONCEPTO] usando una analogía simple, luego una explicación técnica y finalmente un ejemplo mínimo en Java.
No me expliques la anotación todavía. Primero explicame qué tendría que hacer manualmente en Java para resolver este problema, y recién después cómo Spring Boot lo simplifica.
Compará Controller, Service y Repository en una tabla. Para cada capa indicá responsabilidad, ejemplo de código y errores típicos de principiante.
Haceme 10 preguntas cortas sobre [TEMA]. No me des las respuestas hasta que yo conteste. Después corregime con explicación.
Te paso un error de Spring Boot. Quiero que lo diagnostiques por capas: compilación, dependencias, configuración, runtime, código. No propongas cambios hasta identificar la causa más probable.
Revisá este controller/service/repository. Marcá acoplamientos incorrectos, responsabilidades mezcladas, nombres confusos y tests faltantes. No reescribas todo: primero explicá problemas.
Generá tests JUnit para esta clase. Cubrí caso exitoso, datos inválidos y borde. Explicá qué prueba cada test y qué NO está probando.
Leé el proyecto. No modifiques archivos todavía. Primero explicá la arquitectura actual, comandos de build/test y dónde agregarías el próximo endpoint según las capas existentes.
Estoy en el módulo [N]. Ya tengo funcionando [LO QUE FUNCIONA]. Dame el próximo paso mínimo, el código esperado, cómo probarlo con curl y cómo saber si está mal.
Antes de darme código, explicame qué clases voy a crear, por qué existen y cómo se conectan. Luego dame el código por archivo, en orden de creación.
Este código funciona pero está mezclado. Proponé un refactor en Controller/Service/Repository. Mantené el comportamiento y decime qué tests deberían seguir pasando.
Actuá como revisor técnico. Dame un checklist para aprobar mi proyecto Spring Boot básico: endpoints, validación, errores, tests, configuración, logs y README.
Un ejercicio chico, suficientemente real para aprender las capas.
POST /tickets crea ticket.GET /tickets/{id} busca por id.GET /tickets lista tickets.POST /tickets/{id}/pay marca como pagado.GET /actuator/health muestra salud.No es un curso de Git. Es el circuito básico para guardar avances, subirlos a GitHub y trabajar con Codex o Claude Code sin romper el proyecto.
Para empezar no necesitás saber ramas complejas, rebase ni pull requests. Necesitás entender este flujo simple:
Git es como guardar checkpoints en un videojuego. GitHub es la copia en la nube. Si algo sale mal, volvés a un checkpoint anterior.
No uses agentes sobre una carpeta sin Git. Antes de pedir cambios, dejá el proyecto en estado limpio: git status sin archivos pendientes.
New repository.ticket-api-spring-boot.Add README. Si ya tenés proyecto local generado por Spring Initializr, mejor dejarlo vacío para conectar después.https://github.com/usuario/ticket-api-spring-boot.git.# Elegí una carpeta de trabajo cd C:\Work # Clonar el repo desde GitHub git clone https://github.com/usuario/ticket-api-spring-boot.git # Entrar al proyecto cd ticket-api-spring-boot # Verificar estado git status # Abrir en VS Code, si lo usás code .
.git: ahí vive el historial.git status debería decir que no hay cambios pendientes.No trabajes en ZIPs descargados manualmente. Cloná con Git. Si no hay .git, no tenés historial local real.
mvn test.# 1. Ver archivos modificados git status # 2. Ver cambios concretos git diff # 3. Ejecutar tests mvn test # 4. Preparar archivos para commit git add . # 5. Crear checkpoint local git commit -m "feat: agregar endpoint de tickets" # 6. Subir a GitHub git push origin main
Para arrancar, usá mensajes simples: feat: para funcionalidad, fix: para correcciones, docs: para documentación.
Este caso pasa mucho con Spring Initializr: descargás el ZIP, lo descomprimís, abrís el proyecto y recién después querés subirlo a GitHub.
# Dentro de la carpeta del proyecto git init git add . git commit -m "chore: proyecto inicial Spring Boot" # Conectar con el repo vacío creado en GitHub git remote add origin https://github.com/usuario/ticket-api-spring-boot.git # Definir rama principal y subir git branch -M main git push -u origin main
.gitignore.target/, .idea/, .vscode/ si no corresponde..env.mvn test.Si un archivo tiene credenciales, no va al repo. Ni público ni privado. Usá variables de entorno o archivos locales ignorados por Git.
git status. Si hay cambios, commitealos o descartalos. No mezcles trabajos.mvn test. Si falla, no hay commit.Leé este repo Spring Boot. No modifiques archivos todavía. Explicame estructura, paquetes principales, comandos de build/test/run y riesgos visibles.
Implementá solo el cambio descripto: [CAMBIO]. No toques dependencias ni estructura global. Agregá o actualizá tests. Al final indicá archivos modificados y cómo probar.
Revisá el diff actual como si fueras code reviewer. Señalá errores, cambios innecesarios, tests faltantes y posibles regresiones. No hagas commit todavía.
Proponé un mensaje de commit corto con formato tipo conventional commits para estos cambios. No inventes alcance; basate sólo en el diff.
| Validación | Criterio mínimo |
|---|---|
| Build | mvn clean test pasa. |
| Run | La app arranca con mvn spring-boot:run. |
| HTTP | Todos los endpoints responden con status correcto. |
| Validación | Requests inválidos devuelven 400 con JSON claro. |
| Negocio | No permite pagar dos veces ni pagar ticket inexistente. |
| Tests | Hay tests de service y al menos un test de controller. |
| Operación | /actuator/health responde UP. |
| README | Incluye instalación, comandos, endpoints y ejemplos curl. |