Skip to content

Repository files navigation

Proyecto: Gestión de Clientes con Spring Boot

Un proyecto educativo diseñado para estudiantes de segundo año de Ingeniería en Computación que enseña los principios fundamentales de Spring Framework, decoradores básicos y de validación, buenas prácticas de APIs REST con HTTP status codes, y manejo centralizado de errores.

Este ejercicio corresponde al ramo Fullstack I para estudiantes de Ingeniería en Informática de DUOC UC.

📚 Objetivos de Aprendizaje

  • Comprender la arquitectura de una aplicación Spring Boot
  • Dominar decoradores (anotaciones) básicas y de validación
  • Implementar APIs REST siguiendo buenas prácticas
  • Usar códigos de estado HTTP apropiados
  • Implementar manejo centralizado de excepciones
  • Aplicar inyección de dependencias
  • Utilizar el patrón de diseño CSR (Controller-Service-Repository): adaptación de MVC para Spring Boot

🔧 Requisitos Previos

  • Java 21 o superior
  • Maven 3.6+ para gestión de dependencias
  • IDE recomendado: IntelliJ IDEA, Visual Studio Code o Eclipse
  • Conocimientos básicos de Java y POO
  • Conceptos básicos de APIs REST y HTTP

📦 Dependencias Principales

<!-- Spring Boot Starter Web (REST Controllers y servidores web) -->
spring-boot-starter-web
<!-- Spring Boot Starter Validation (Validación de datos) -->
spring-boot-starter-validation
<!-- Lombok (Generación automática de getters, setters, etc.) -->
lombok
<!-- Spring Boot Starter Test (Pruebas unitarias) -->
spring-boot-starter-test

🏗️ Estructura del Proyecto

clientes/
├── src/
│ ├── main/
│ │ ├── java/com/duoc/clientes/
│ │ │ ├── ClientesApplication.java # Punto de entrada
│ │ │ ├── controller/
│ │ │ │ └── ClientesController.java # Endpoints REST
│ │ │ ├── model/
│ │ │ │ └── ClientesModel.java # Entidad con validaciones
│ │ │ ├── service/
│ │ │ │ └── ClientesService.java # Lógica de negocio
│ │ │ ├── repository/
│ │ │ │ └── ClientesRepository.java # Acceso a datos
│ │ │ └── exception/
│ │ │ └── GlobalExceptionHandler.java # Manejo de errores
│ │ └── resources/
│ │ └── application.properties # Configuración
│ └── test/
│ └── ClientesApplicationTests.java # Pruebas
└── pom.xml # Configuración Maven

🎯 Conceptos Clave

1. Decoradores (Anotaciones) Básicos de Spring

Las anotaciones son etiquetas especiales que proporcionan metadatos sobre el programa, no affecting directly the operation of the code but providing information to the framework.

@SpringBootApplication

Ubicación: ClientesApplication.java

@SpringBootApplicationpublicclassClientesApplication {
publicstaticvoidmain(String[] args) {
SpringApplication.run(ClientesApplication.class, args);
}
}

¿Qué hace? Combina tres anotaciones en una:

  • @Configuration: Marca la clase como fuente de definiciones de beans
  • @EnableAutoConfiguration: Permite que Spring Boot configure automáticamente la aplicación basándose en las dependencias
  • @ComponentScan: Escanea el paquete actual y subpaquetes buscando componentes anotados

@RestController

Ubicación: ClientesController.java

@RestController@RequestMapping("/api/v1/clientes")
publicclassClientesController {
// ...
}

¿Qué hace?

  • Marca la clase como controlador REST
  • Equivalente a @Controller + @ResponseBody
  • Los métodos retornan datos serializados (JSON) automáticamente
  • @RequestMapping("/api/v1/clientes"): Define la ruta base para todos los endpoints

@Autowired

Ubicación: ClientesController.java

@AutowiredprivateClientesServiceclientesService;

¿Qué hace?

  • Inyección de Dependencias: Spring inyecta automáticamente una instancia de ClientesService
  • No necesitas crear la instancia manualmente con new
  • Spring gestiona el ciclo de vida del objeto
  • Promueve el desacoplamiento y facilita las pruebas

@Service

Ubicación: ClientesService.java

@ServicepublicclassClientesService {
// ...
}

¿Qué hace?

  • Marca la clase como un bean de servicio (componente del negocio)
  • Contiene la lógica empresarial
  • Spring la detecta automáticamente y la registra como bean
  • Se puede inyectar en otros componentes

@Repository

Ubicación: ClientesRepository.java

@RepositorypublicclassClientesRepository {
// ...
}

¿Qué hace?

  • Marca la clase como un repositorio (capa de acceso a datos)
  • Responsable de CRUD (Create, Read, Update, Delete)
  • En este proyecto, usa un ArrayList en memoria (simulación)
  • En proyectos reales, se conectaría a una base de datos

2. Decoradores de Validación (Validación de Datos)

Garantizan que los datos recibidos cumplan con reglas específicas antes de procesarlos.

Ubicación: ClientesModel.java

@Data@NoArgsConstructor@AllArgsConstructorpublicclassClientesModel {
@NotNull(message = "El nombre no puede ser null")
@NotBlank(message = "El nombre no puede estar vacio")
privateStringnombre;
@NotNull(message = "El correo no puede ser null")
@NotBlank(message = "El correo no puede estar vacio")
privateStringcorreo;
@NotNull(message = "La edad no puede ser null")
@Positive(message = "La edad debe ser mayor a cero")
privateIntegeredad;
}

@NotNull

  • Valida: El valor no puede ser null
  • Tipo: Cualquier tipo de datos
  • Diferencia con @NotBlank: Permite strings vacíos ""

@NotBlank

  • Valida: El string no puede ser null ni estar vacío
  • Trim: Considera espacios en blanco como vacío
  • Tipo: Solo strings

@Positive

  • Valida: El número debe ser mayor que 0
  • Tipo: Números (Integer, Double, BigDecimal, etc.)

Decoradores de Lombok

@Data// Genera: getters, setters, equals(), hashCode(), toString()@NoArgsConstructor// Genera constructor sin argumentos@AllArgsConstructor// Genera constructor con todos los campos

3. Mapeo de Endpoints REST

Verbos HTTP y Decoradores Correspondientes

@RestController@RequestMapping("/api/v1/clientes")
publicclassClientesController {
// GET - Obtener todos los clientes@GetMappingpublicResponseEntity<List<ClientesModel>> listarClientes() { }
// POST - Crear nuevo cliente@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@Valid@RequestBodyClientesModelcliente
) { }
// DELETE - Eliminar cliente por correo@DeleteMapping("{correo}")
publicResponseEntity<String> eliminarCliente(
@PathVariableStringcorreo
) { }
}
DecoratorVerbo HTTPOperaciónDescripción
@GetMappingGETReadObtiene datos sin modificarlos
@PostMappingPOSTCreateCrea un nuevo recurso
@PutMappingPUTUpdateActualiza completamente un recurso
@PatchMappingPATCHPartial UpdateActualiza parcialmente un recurso
@DeleteMappingDELETEDeleteElimina un recurso

Parámetros Importantes

// @RequestBody: Mapea el JSON del request al objeto@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@RequestBodyClientesModelcliente
) { }
// @Valid: Activa la validación de datos@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@Valid@RequestBodyClientesModelcliente
) { }
// @PathVariable: Extrae valores de la ruta@DeleteMapping("{correo}")
publicResponseEntity<String> eliminarCliente(
@PathVariableStringcorreo
) { }

4. HTTP Status Codes (Códigos de Estado)

Los códigos HTTP comunican al cliente el resultado de su request.

Códigos Utilizados en Este Proyecto

CódigoNombreSignificadoCuándo Usar
200OKLa solicitud fue exitosaGET exitoso, DELETE exitoso
201CreatedRecurso creado exitosamentePOST que crea un recurso
400Bad RequestSolicitud inválida o error en validaciónDatos inválidos, errores de negocio
404Not FoundRecurso no encontradoGET de recurso inexistente
500Internal Server ErrorError del servidorExcepciones no controladas

Ejemplo de Uso en el Proyecto

// GET exitoso (200)@GetMappingpublicResponseEntity<List<ClientesModel>> listarClientes() {
returnResponseEntity.status(200).body(clientesService.getClientes());
}
// POST exitoso (201)@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(@Valid@RequestBodyClientesModelcliente) {
returnResponseEntity.status(201).body(clientesService.saveCliente(cliente));
}
// Error en validación (400)// Manejado automáticamente por GlobalExceptionHandler

5. Manejo Centralizado de Excepciones

En lugar de manejar errores en cada endpoint, Spring permite centralizar la gestión en una clase especial.

Ubicación: GlobalExceptionHandler.java

@RestControllerAdvicepublicclassGlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
publicResponseEntity<Map<String, String>> handleValidationErrors(
MethodArgumentNotValidExceptionex
) {
Map<String, String> errores = newHashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errores.put(error.getField(), error.getDefaultMessage())
);
returnResponseEntity.status(HttpStatus.BAD_REQUEST).body(errores);
}
}

¿Cómo funciona?

  1. @RestControllerAdvice: Marca la clase como handler global de excepciones
  2. @ExceptionHandler: Define qué tipo de excepción maneja este método
  3. Cuando alguien envía un cliente sin nombre, Spring:
    • Lanza MethodArgumentNotValidException
    • GlobalExceptionHandler lo captura
    • Retorna un JSON con los errores específicos

Respuesta de Error (ejemplo):

{
"nombre": "El nombre no puede estar vacio",
"edad": "La edad debe ser mayor a cero"
}

🚀 Crear un Nuevo Proyecto con Spring Initializr

Si deseas crear un proyecto similar desde cero, puedes usar Spring Initializr:

Opción 1: Web (Recomendado para principiantes)

  1. Accede a: start.spring.io
  2. Configura:
    • Project: Maven Project
    • Language: Java
    • Spring Boot: 4.0.4 o superior
    • Group: com.duoc
    • Artifact: clientes
    • Java Version: 21
  3. Dependencias (Add Dependencies):
    • Spring Web
    • Spring Boot Validation
    • Lombok
  4. Click "Generate"
  5. Descomprime y abre en tu IDE favorito

Opción 2: Línea de Comandos

curl https://start.spring.io/starter.zip \
-d dependencies=web,validation,lombok \
-d javaVersion=21 \
-d bootVersion=4.0.4 \
-d groupId=com.duoc \
-d artifactId=clientes \
-o clientes.zip
unzip clientes.zip
cd clientes

✅ Paso a Paso para Ejecutar el Proyecto

Paso 1: Clonar o Descargar el Proyecto

cd tu-directorio
# Si es un repositorio git:
git clone <repositorio>cd clientes

Paso 2: Verificar Java

java -version
# Debe mostrar Java 21 o superior

Paso 3: Compilar el Proyecto

mvn clean compile
  • clean: Elimina compilaciones anteriores
  • compile: Compila el código fuente

Paso 4: Ejecutar la Aplicación

mvn spring-boot:run

Salida esperada:

[INFO] Started ClientesApplication in 2.5 seconds

La aplicación estará disponible en: http://localhost:8080


🧪 Pruebas de Endpoints

Herramientas Recomendadas

  • Postman: Interfaz gráfica completa
  • cURL: Línea de comandos (incluido en macOS/Linux)
  • Thunder Client: Extensión de VS Code
  • Insomnia: Similar a Postman

Ejemplos con cURL

1. Listar todos los clientes (GET)

curl -X GET http://localhost:8080/api/v1/clientes

Respuesta esperada (200):

[]

2. Crear un cliente válido (POST)

curl -X POST http://localhost:8080/api/v1/clientes \
-H "Content-Type: application/json" \
-d '{ "nombre": "Juan Pérez", "correo": "juan@example.com", "edad": 25 }'

Respuesta esperada (201):

{
"nombre": "Juan Pérez",
"correo": "juan@example.com",
"edad": 25
}

3. Intentar crear cliente con datos inválidos (POST)

curl -X POST http://localhost:8080/api/v1/clientes \
-H "Content-Type: application/json" \
-d '{ "nombre": "", "correo": "juan@example.com", "edad": -5 }'

Respuesta esperada (400):

{
"nombre": "El nombre no puede estar vacio",
"edad": "La edad debe ser mayor a cero"
}

4. Listar clientes después de crear uno (GET)

curl -X GET http://localhost:8080/api/v1/clientes

Respuesta esperada (200):

[
{
"nombre": "Juan Pérez",
"correo": "juan@example.com",
"edad": 25
}
]

5. Eliminar un cliente (DELETE)

curl -X DELETE http://localhost:8080/api/v1/clientes/juan@example.com

Respuesta esperada (200):

"Cliente eliminado"

📊 Flujo de Datos en la Aplicación

┌─────────────────────────────────────────────────────────────────┐
│ CLIENTE (Postman, cURL) │
└────────────────────────┬──────────────────────────────────────────┘
│ 1. Envía JSON
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesController │
│ - Recibe request HTTP │
│ - Mapea JSON → ClientesModel (@RequestBody) │
│ - Valida datos (@Valid) │
│ - Si hay errores → GlobalExceptionHandler (captura) │
└────────────────────────┬──────────────────────────────────────────┘
│ 2. Si válido, llama servicio
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesService │
│ - Contiene lógica de negocio │
│ - Procesa los datos │
│ - Llama al repositorio │
└────────────────────────┬──────────────────────────────────────────┘
│ 3. Persistencia
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesRepository │
│ - ArrayList (simula base de datos en memoria) │
│ - CRUD: Crear, Leer, Actualizar, Eliminar │
│ - Retorna datos procesados │
└────────────────────────┬──────────────────────────────────────────┘
│ 4. Respuesta
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesController │
│ - ResponseEntity with HTTP Status (200, 201, 400) │
│ - Serializa objeto → JSON │
└────────────────────────┬──────────────────────────────────────────┘
│ 5. Envía JSON
▼
┌─────────────────────────────────────────────────────────────────┐
│ CLIENTE (Recibe respuesta) │
└─────────────────────────────────────────────────────────────────┘

🔄 Patrón de Diseño CSR (Controller-Service-Repository)

CSR es la versión adaptada del patrón MVC específicamente para Spring Boot y APIs REST.

CapaComponenteArchivoResponsabilidad
PresentaciónControllerClientesController.javaMapea rutas HTTP, recibe requests, retorna responses
Lógica de NegocioServiceClientesService.javaContiene la lógica de negocio, orquesta operaciones
DatosRepositoryClientesRepository.javaAcceso a datos, persistencia, operaciones CRUD
DatosModelClientesModel.javaRepresenta la entidad con atributos y validaciones

¿Por qué CSR en lugar de MVC?

En aplicaciones REST con Spring Boot:

  • No hay "View" tradicional (HTML) → La respuesta es JSON
  • La separación de responsabilidades es más clara
  • Service maneja la lógica de negocio (no el Controller)
  • Repository aísla el acceso a datos
  • Facilita testing y mantenimiento

Ventajas del Patrón CSR

✅ Código más limpio y organizado
✅ Fácil de testear (inyección de dependencias)
✅ Escalable (añadir funcionalidades sin afectar otras capas)
✅ Reutilizable (Service puede usarse desde múltiples Controllers)
✅ Mantenible (cambios aislados por capa)


💡 Buenas Prácticas Implementadas

1. Separación de Responsabilidades

Cada clase tiene una único propósito claro:

  • Controller: Mapeo HTTP
  • Service: Lógica de negocio
  • Repository: Acceso a datos

2. Inyección de Dependencias

@AutowiredprivateClientesServiceclientesService;

Facilita: Testing, desacoplamiento, mantenibilidad

3. Validación de Datos a la Entrada

@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(@Valid@RequestBodyClientesModelcliente)

Previene procesamiento de datos inválidos

4. Códigos de Estado HTTP Apropiados

  • 200: Operación exitosa
  • 201: Recurso creado
  • 400: Solicitud inválida
  • 500: Error del servidor

5. Manejo Centralizado de Excepciones

En lugar de try-catch en cada método, usar @RestControllerAdvice

6. Versionamiento de API

/api/v1/clientes permite evitar conflictos al cambiar la API


📖 Ejercicios Propuestos

Nivel 1: Básico

  1. Agregar un nuevo campo telefono a ClientesModel con validación
  2. Crear un endpoint PUT para actualizar cliente
  3. Agregar validación de email usando @Email

Nivel 2: Intermedio

  1. Agregar manejo de excepción personalizada (ej: ClienteNoEncontradoException)
  2. Implementar búsqueda de cliente por correo (GET con parámetro)
  3. Agregar información de error más detallada en respuestas

📝 Recursos Adicionales


🐛 Solución de Problemas Comunes

Error: "Port 8080 already in use"

# Cambiar puerto en application.properties:
server.port=8081

Error: "Field clientesService required a bean of type"

Este error significa que la inyección de dependencias no encontró el bean. Asegurate:

  • La clase ClientesService tiene @Service
  • El controlador tiene @Autowired
  • Spring escanea el paquete correcto

Error: "JSON parse error"

El JSON enviado tiene formato inválido. Verifica:

  • Comillas dobles en propiedades
  • Tipos de datos correctos
  • No hay caracteres de escape faltantes

📄 Licencia

Este proyecto es de código abierto y está disponible bajo la licencia MIT, diseñado para propósitos educativos.


Última actualización: 28 de marzo de 2026

About

Proyecto educativo de Fullstack I para estudiantes de segundo año de Ingeniería en Informática: API REST en Spring Boot para gestión de clientes, aplicando validaciones, arquitectura por capas y buenas prácticas HTTP.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - raicerk/fullstack-i-clientes: Proyecto educativo de Fullstack I para estudiantes de segundo año de Ingeniería en Informática: API REST en Spring Boot para gestión de clientes, aplicando validaciones, arquitectura por capas y buenas prácticas HTTP. · GitHub
Skip to content

Repository files navigation

Proyecto: Gestión de Clientes con Spring Boot

Un proyecto educativo diseñado para estudiantes de segundo año de Ingeniería en Computación que enseña los principios fundamentales de Spring Framework, decoradores básicos y de validación, buenas prácticas de APIs REST con HTTP status codes, y manejo centralizado de errores.

Este ejercicio corresponde al ramo Fullstack I para estudiantes de Ingeniería en Informática de DUOC UC.

📚 Objetivos de Aprendizaje

  • Comprender la arquitectura de una aplicación Spring Boot
  • Dominar decoradores (anotaciones) básicas y de validación
  • Implementar APIs REST siguiendo buenas prácticas
  • Usar códigos de estado HTTP apropiados
  • Implementar manejo centralizado de excepciones
  • Aplicar inyección de dependencias
  • Utilizar el patrón de diseño CSR (Controller-Service-Repository): adaptación de MVC para Spring Boot

🔧 Requisitos Previos

  • Java 21 o superior
  • Maven 3.6+ para gestión de dependencias
  • IDE recomendado: IntelliJ IDEA, Visual Studio Code o Eclipse
  • Conocimientos básicos de Java y POO
  • Conceptos básicos de APIs REST y HTTP

📦 Dependencias Principales

<!-- Spring Boot Starter Web (REST Controllers y servidores web) -->
spring-boot-starter-web
<!-- Spring Boot Starter Validation (Validación de datos) -->
spring-boot-starter-validation
<!-- Lombok (Generación automática de getters, setters, etc.) -->
lombok
<!-- Spring Boot Starter Test (Pruebas unitarias) -->
spring-boot-starter-test

🏗️ Estructura del Proyecto

clientes/
├── src/
│ ├── main/
│ │ ├── java/com/duoc/clientes/
│ │ │ ├── ClientesApplication.java # Punto de entrada
│ │ │ ├── controller/
│ │ │ │ └── ClientesController.java # Endpoints REST
│ │ │ ├── model/
│ │ │ │ └── ClientesModel.java # Entidad con validaciones
│ │ │ ├── service/
│ │ │ │ └── ClientesService.java # Lógica de negocio
│ │ │ ├── repository/
│ │ │ │ └── ClientesRepository.java # Acceso a datos
│ │ │ └── exception/
│ │ │ └── GlobalExceptionHandler.java # Manejo de errores
│ │ └── resources/
│ │ └── application.properties # Configuración
│ └── test/
│ └── ClientesApplicationTests.java # Pruebas
└── pom.xml # Configuración Maven

🎯 Conceptos Clave

1. Decoradores (Anotaciones) Básicos de Spring

Las anotaciones son etiquetas especiales que proporcionan metadatos sobre el programa, no affecting directly the operation of the code but providing information to the framework.

@SpringBootApplication

Ubicación: ClientesApplication.java

@SpringBootApplicationpublicclassClientesApplication {
publicstaticvoidmain(String[] args) {
SpringApplication.run(ClientesApplication.class, args);
}
}

¿Qué hace? Combina tres anotaciones en una:

  • @Configuration: Marca la clase como fuente de definiciones de beans
  • @EnableAutoConfiguration: Permite que Spring Boot configure automáticamente la aplicación basándose en las dependencias
  • @ComponentScan: Escanea el paquete actual y subpaquetes buscando componentes anotados

@RestController

Ubicación: ClientesController.java

@RestController@RequestMapping("/api/v1/clientes")
publicclassClientesController {
// ...
}

¿Qué hace?

  • Marca la clase como controlador REST
  • Equivalente a @Controller + @ResponseBody
  • Los métodos retornan datos serializados (JSON) automáticamente
  • @RequestMapping("/api/v1/clientes"): Define la ruta base para todos los endpoints

@Autowired

Ubicación: ClientesController.java

@AutowiredprivateClientesServiceclientesService;

¿Qué hace?

  • Inyección de Dependencias: Spring inyecta automáticamente una instancia de ClientesService
  • No necesitas crear la instancia manualmente con new
  • Spring gestiona el ciclo de vida del objeto
  • Promueve el desacoplamiento y facilita las pruebas

@Service

Ubicación: ClientesService.java

@ServicepublicclassClientesService {
// ...
}

¿Qué hace?

  • Marca la clase como un bean de servicio (componente del negocio)
  • Contiene la lógica empresarial
  • Spring la detecta automáticamente y la registra como bean
  • Se puede inyectar en otros componentes

@Repository

Ubicación: ClientesRepository.java

@RepositorypublicclassClientesRepository {
// ...
}

¿Qué hace?

  • Marca la clase como un repositorio (capa de acceso a datos)
  • Responsable de CRUD (Create, Read, Update, Delete)
  • En este proyecto, usa un ArrayList en memoria (simulación)
  • En proyectos reales, se conectaría a una base de datos

2. Decoradores de Validación (Validación de Datos)

Garantizan que los datos recibidos cumplan con reglas específicas antes de procesarlos.

Ubicación: ClientesModel.java

@Data@NoArgsConstructor@AllArgsConstructorpublicclassClientesModel {
@NotNull(message = "El nombre no puede ser null")
@NotBlank(message = "El nombre no puede estar vacio")
privateStringnombre;
@NotNull(message = "El correo no puede ser null")
@NotBlank(message = "El correo no puede estar vacio")
privateStringcorreo;
@NotNull(message = "La edad no puede ser null")
@Positive(message = "La edad debe ser mayor a cero")
privateIntegeredad;
}

@NotNull

  • Valida: El valor no puede ser null
  • Tipo: Cualquier tipo de datos
  • Diferencia con @NotBlank: Permite strings vacíos ""

@NotBlank

  • Valida: El string no puede ser null ni estar vacío
  • Trim: Considera espacios en blanco como vacío
  • Tipo: Solo strings

@Positive

  • Valida: El número debe ser mayor que 0
  • Tipo: Números (Integer, Double, BigDecimal, etc.)

Decoradores de Lombok

@Data// Genera: getters, setters, equals(), hashCode(), toString()@NoArgsConstructor// Genera constructor sin argumentos@AllArgsConstructor// Genera constructor con todos los campos

3. Mapeo de Endpoints REST

Verbos HTTP y Decoradores Correspondientes

@RestController@RequestMapping("/api/v1/clientes")
publicclassClientesController {
// GET - Obtener todos los clientes@GetMappingpublicResponseEntity<List<ClientesModel>> listarClientes() { }
// POST - Crear nuevo cliente@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@Valid@RequestBodyClientesModelcliente
) { }
// DELETE - Eliminar cliente por correo@DeleteMapping("{correo}")
publicResponseEntity<String> eliminarCliente(
@PathVariableStringcorreo
) { }
}
DecoratorVerbo HTTPOperaciónDescripción
@GetMappingGETReadObtiene datos sin modificarlos
@PostMappingPOSTCreateCrea un nuevo recurso
@PutMappingPUTUpdateActualiza completamente un recurso
@PatchMappingPATCHPartial UpdateActualiza parcialmente un recurso
@DeleteMappingDELETEDeleteElimina un recurso

Parámetros Importantes

// @RequestBody: Mapea el JSON del request al objeto@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@RequestBodyClientesModelcliente
) { }
// @Valid: Activa la validación de datos@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@Valid@RequestBodyClientesModelcliente
) { }
// @PathVariable: Extrae valores de la ruta@DeleteMapping("{correo}")
publicResponseEntity<String> eliminarCliente(
@PathVariableStringcorreo
) { }

4. HTTP Status Codes (Códigos de Estado)

Los códigos HTTP comunican al cliente el resultado de su request.

Códigos Utilizados en Este Proyecto

CódigoNombreSignificadoCuándo Usar
200OKLa solicitud fue exitosaGET exitoso, DELETE exitoso
201CreatedRecurso creado exitosamentePOST que crea un recurso
400Bad RequestSolicitud inválida o error en validaciónDatos inválidos, errores de negocio
404Not FoundRecurso no encontradoGET de recurso inexistente
500Internal Server ErrorError del servidorExcepciones no controladas

Ejemplo de Uso en el Proyecto

// GET exitoso (200)@GetMappingpublicResponseEntity<List<ClientesModel>> listarClientes() {
returnResponseEntity.status(200).body(clientesService.getClientes());
}
// POST exitoso (201)@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(@Valid@RequestBodyClientesModelcliente) {
returnResponseEntity.status(201).body(clientesService.saveCliente(cliente));
}
// Error en validación (400)// Manejado automáticamente por GlobalExceptionHandler

5. Manejo Centralizado de Excepciones

En lugar de manejar errores en cada endpoint, Spring permite centralizar la gestión en una clase especial.

Ubicación: GlobalExceptionHandler.java

@RestControllerAdvicepublicclassGlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
publicResponseEntity<Map<String, String>> handleValidationErrors(
MethodArgumentNotValidExceptionex
) {
Map<String, String> errores = newHashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errores.put(error.getField(), error.getDefaultMessage())
);
returnResponseEntity.status(HttpStatus.BAD_REQUEST).body(errores);
}
}

¿Cómo funciona?

  1. @RestControllerAdvice: Marca la clase como handler global de excepciones
  2. @ExceptionHandler: Define qué tipo de excepción maneja este método
  3. Cuando alguien envía un cliente sin nombre, Spring:
    • Lanza MethodArgumentNotValidException
    • GlobalExceptionHandler lo captura
    • Retorna un JSON con los errores específicos

Respuesta de Error (ejemplo):

{
"nombre": "El nombre no puede estar vacio",
"edad": "La edad debe ser mayor a cero"
}

🚀 Crear un Nuevo Proyecto con Spring Initializr

Si deseas crear un proyecto similar desde cero, puedes usar Spring Initializr:

Opción 1: Web (Recomendado para principiantes)

  1. Accede a: start.spring.io
  2. Configura:
    • Project: Maven Project
    • Language: Java
    • Spring Boot: 4.0.4 o superior
    • Group: com.duoc
    • Artifact: clientes
    • Java Version: 21
  3. Dependencias (Add Dependencies):
    • Spring Web
    • Spring Boot Validation
    • Lombok
  4. Click "Generate"
  5. Descomprime y abre en tu IDE favorito

Opción 2: Línea de Comandos

curl https://start.spring.io/starter.zip \
-d dependencies=web,validation,lombok \
-d javaVersion=21 \
-d bootVersion=4.0.4 \
-d groupId=com.duoc \
-d artifactId=clientes \
-o clientes.zip
unzip clientes.zip
cd clientes

✅ Paso a Paso para Ejecutar el Proyecto

Paso 1: Clonar o Descargar el Proyecto

cd tu-directorio
# Si es un repositorio git:
git clone <repositorio>cd clientes

Paso 2: Verificar Java

java -version
# Debe mostrar Java 21 o superior

Paso 3: Compilar el Proyecto

mvn clean compile
  • clean: Elimina compilaciones anteriores
  • compile: Compila el código fuente

Paso 4: Ejecutar la Aplicación

mvn spring-boot:run

Salida esperada:

[INFO] Started ClientesApplication in 2.5 seconds

La aplicación estará disponible en: http://localhost:8080


🧪 Pruebas de Endpoints

Herramientas Recomendadas

  • Postman: Interfaz gráfica completa
  • cURL: Línea de comandos (incluido en macOS/Linux)
  • Thunder Client: Extensión de VS Code
  • Insomnia: Similar a Postman

Ejemplos con cURL

1. Listar todos los clientes (GET)

curl -X GET http://localhost:8080/api/v1/clientes

Respuesta esperada (200):

[]

2. Crear un cliente válido (POST)

curl -X POST http://localhost:8080/api/v1/clientes \
-H "Content-Type: application/json" \
-d '{ "nombre": "Juan Pérez", "correo": "juan@example.com", "edad": 25 }'

Respuesta esperada (201):

{
"nombre": "Juan Pérez",
"correo": "juan@example.com",
"edad": 25
}

3. Intentar crear cliente con datos inválidos (POST)

curl -X POST http://localhost:8080/api/v1/clientes \
-H "Content-Type: application/json" \
-d '{ "nombre": "", "correo": "juan@example.com", "edad": -5 }'

Respuesta esperada (400):

{
"nombre": "El nombre no puede estar vacio",
"edad": "La edad debe ser mayor a cero"
}

4. Listar clientes después de crear uno (GET)

curl -X GET http://localhost:8080/api/v1/clientes

Respuesta esperada (200):

[
{
"nombre": "Juan Pérez",
"correo": "juan@example.com",
"edad": 25
}
]

5. Eliminar un cliente (DELETE)

curl -X DELETE http://localhost:8080/api/v1/clientes/juan@example.com

Respuesta esperada (200):

"Cliente eliminado"

📊 Flujo de Datos en la Aplicación

┌─────────────────────────────────────────────────────────────────┐
│ CLIENTE (Postman, cURL) │
└────────────────────────┬──────────────────────────────────────────┘
│ 1. Envía JSON
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesController │
│ - Recibe request HTTP │
│ - Mapea JSON → ClientesModel (@RequestBody) │
│ - Valida datos (@Valid) │
│ - Si hay errores → GlobalExceptionHandler (captura) │
└────────────────────────┬──────────────────────────────────────────┘
│ 2. Si válido, llama servicio
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesService │
│ - Contiene lógica de negocio │
│ - Procesa los datos │
│ - Llama al repositorio │
└────────────────────────┬──────────────────────────────────────────┘
│ 3. Persistencia
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesRepository │
│ - ArrayList (simula base de datos en memoria) │
│ - CRUD: Crear, Leer, Actualizar, Eliminar │
│ - Retorna datos procesados │
└────────────────────────┬──────────────────────────────────────────┘
│ 4. Respuesta
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesController │
│ - ResponseEntity with HTTP Status (200, 201, 400) │
│ - Serializa objeto → JSON │
└────────────────────────┬──────────────────────────────────────────┘
│ 5. Envía JSON
▼
┌─────────────────────────────────────────────────────────────────┐
│ CLIENTE (Recibe respuesta) │
└─────────────────────────────────────────────────────────────────┘

🔄 Patrón de Diseño CSR (Controller-Service-Repository)

CSR es la versión adaptada del patrón MVC específicamente para Spring Boot y APIs REST.

CapaComponenteArchivoResponsabilidad
PresentaciónControllerClientesController.javaMapea rutas HTTP, recibe requests, retorna responses
Lógica de NegocioServiceClientesService.javaContiene la lógica de negocio, orquesta operaciones
DatosRepositoryClientesRepository.javaAcceso a datos, persistencia, operaciones CRUD
DatosModelClientesModel.javaRepresenta la entidad con atributos y validaciones

¿Por qué CSR en lugar de MVC?

En aplicaciones REST con Spring Boot:

  • No hay "View" tradicional (HTML) → La respuesta es JSON
  • La separación de responsabilidades es más clara
  • Service maneja la lógica de negocio (no el Controller)
  • Repository aísla el acceso a datos
  • Facilita testing y mantenimiento

Ventajas del Patrón CSR

✅ Código más limpio y organizado
✅ Fácil de testear (inyección de dependencias)
✅ Escalable (añadir funcionalidades sin afectar otras capas)
✅ Reutilizable (Service puede usarse desde múltiples Controllers)
✅ Mantenible (cambios aislados por capa)


💡 Buenas Prácticas Implementadas

1. Separación de Responsabilidades

Cada clase tiene una único propósito claro:

  • Controller: Mapeo HTTP
  • Service: Lógica de negocio
  • Repository: Acceso a datos

2. Inyección de Dependencias

@AutowiredprivateClientesServiceclientesService;

Facilita: Testing, desacoplamiento, mantenibilidad

3. Validación de Datos a la Entrada

@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(@Valid@RequestBodyClientesModelcliente)

Previene procesamiento de datos inválidos

4. Códigos de Estado HTTP Apropiados

  • 200: Operación exitosa
  • 201: Recurso creado
  • 400: Solicitud inválida
  • 500: Error del servidor

5. Manejo Centralizado de Excepciones

En lugar de try-catch en cada método, usar @RestControllerAdvice

6. Versionamiento de API

/api/v1/clientes permite evitar conflictos al cambiar la API


📖 Ejercicios Propuestos

Nivel 1: Básico

  1. Agregar un nuevo campo telefono a ClientesModel con validación
  2. Crear un endpoint PUT para actualizar cliente
  3. Agregar validación de email usando @Email

Nivel 2: Intermedio

  1. Agregar manejo de excepción personalizada (ej: ClienteNoEncontradoException)
  2. Implementar búsqueda de cliente por correo (GET con parámetro)
  3. Agregar información de error más detallada en respuestas

📝 Recursos Adicionales


🐛 Solución de Problemas Comunes

Error: "Port 8080 already in use"

# Cambiar puerto en application.properties:
server.port=8081

Error: "Field clientesService required a bean of type"

Este error significa que la inyección de dependencias no encontró el bean. Asegurate:

  • La clase ClientesService tiene @Service
  • El controlador tiene @Autowired
  • Spring escanea el paquete correcto

Error: "JSON parse error"

El JSON enviado tiene formato inválido. Verifica:

  • Comillas dobles en propiedades
  • Tipos de datos correctos
  • No hay caracteres de escape faltantes

📄 Licencia

Este proyecto es de código abierto y está disponible bajo la licencia MIT, diseñado para propósitos educativos.


Última actualización: 28 de marzo de 2026

About

Proyecto educativo de Fullstack I para estudiantes de segundo año de Ingeniería en Informática: API REST en Spring Boot para gestión de clientes, aplicando validaciones, arquitectura por capas y buenas prácticas HTTP.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - raicerk/fullstack-i-clientes: Proyecto educativo de Fullstack I para estudiantes de segundo año de Ingeniería en Informática: API REST en Spring Boot para gestión de clientes, aplicando validaciones, arquitectura por capas y buenas prácticas HTTP. · GitHub
Skip to content

Repository files navigation

Proyecto: Gestión de Clientes con Spring Boot

Un proyecto educativo diseñado para estudiantes de segundo año de Ingeniería en Computación que enseña los principios fundamentales de Spring Framework, decoradores básicos y de validación, buenas prácticas de APIs REST con HTTP status codes, y manejo centralizado de errores.

Este ejercicio corresponde al ramo Fullstack I para estudiantes de Ingeniería en Informática de DUOC UC.

📚 Objetivos de Aprendizaje

  • Comprender la arquitectura de una aplicación Spring Boot
  • Dominar decoradores (anotaciones) básicas y de validación
  • Implementar APIs REST siguiendo buenas prácticas
  • Usar códigos de estado HTTP apropiados
  • Implementar manejo centralizado de excepciones
  • Aplicar inyección de dependencias
  • Utilizar el patrón de diseño CSR (Controller-Service-Repository): adaptación de MVC para Spring Boot

🔧 Requisitos Previos

  • Java 21 o superior
  • Maven 3.6+ para gestión de dependencias
  • IDE recomendado: IntelliJ IDEA, Visual Studio Code o Eclipse
  • Conocimientos básicos de Java y POO
  • Conceptos básicos de APIs REST y HTTP

📦 Dependencias Principales

<!-- Spring Boot Starter Web (REST Controllers y servidores web) -->
spring-boot-starter-web
<!-- Spring Boot Starter Validation (Validación de datos) -->
spring-boot-starter-validation
<!-- Lombok (Generación automática de getters, setters, etc.) -->
lombok
<!-- Spring Boot Starter Test (Pruebas unitarias) -->
spring-boot-starter-test

🏗️ Estructura del Proyecto

clientes/
├── src/
│ ├── main/
│ │ ├── java/com/duoc/clientes/
│ │ │ ├── ClientesApplication.java # Punto de entrada
│ │ │ ├── controller/
│ │ │ │ └── ClientesController.java # Endpoints REST
│ │ │ ├── model/
│ │ │ │ └── ClientesModel.java # Entidad con validaciones
│ │ │ ├── service/
│ │ │ │ └── ClientesService.java # Lógica de negocio
│ │ │ ├── repository/
│ │ │ │ └── ClientesRepository.java # Acceso a datos
│ │ │ └── exception/
│ │ │ └── GlobalExceptionHandler.java # Manejo de errores
│ │ └── resources/
│ │ └── application.properties # Configuración
│ └── test/
│ └── ClientesApplicationTests.java # Pruebas
└── pom.xml # Configuración Maven

🎯 Conceptos Clave

1. Decoradores (Anotaciones) Básicos de Spring

Las anotaciones son etiquetas especiales que proporcionan metadatos sobre el programa, no affecting directly the operation of the code but providing information to the framework.

@SpringBootApplication

Ubicación: ClientesApplication.java

@SpringBootApplicationpublicclassClientesApplication {
publicstaticvoidmain(String[] args) {
SpringApplication.run(ClientesApplication.class, args);
}
}

¿Qué hace? Combina tres anotaciones en una:

  • @Configuration: Marca la clase como fuente de definiciones de beans
  • @EnableAutoConfiguration: Permite que Spring Boot configure automáticamente la aplicación basándose en las dependencias
  • @ComponentScan: Escanea el paquete actual y subpaquetes buscando componentes anotados

@RestController

Ubicación: ClientesController.java

@RestController@RequestMapping("/api/v1/clientes")
publicclassClientesController {
// ...
}

¿Qué hace?

  • Marca la clase como controlador REST
  • Equivalente a @Controller + @ResponseBody
  • Los métodos retornan datos serializados (JSON) automáticamente
  • @RequestMapping("/api/v1/clientes"): Define la ruta base para todos los endpoints

@Autowired

Ubicación: ClientesController.java

@AutowiredprivateClientesServiceclientesService;

¿Qué hace?

  • Inyección de Dependencias: Spring inyecta automáticamente una instancia de ClientesService
  • No necesitas crear la instancia manualmente con new
  • Spring gestiona el ciclo de vida del objeto
  • Promueve el desacoplamiento y facilita las pruebas

@Service

Ubicación: ClientesService.java

@ServicepublicclassClientesService {
// ...
}

¿Qué hace?

  • Marca la clase como un bean de servicio (componente del negocio)
  • Contiene la lógica empresarial
  • Spring la detecta automáticamente y la registra como bean
  • Se puede inyectar en otros componentes

@Repository

Ubicación: ClientesRepository.java

@RepositorypublicclassClientesRepository {
// ...
}

¿Qué hace?

  • Marca la clase como un repositorio (capa de acceso a datos)
  • Responsable de CRUD (Create, Read, Update, Delete)
  • En este proyecto, usa un ArrayList en memoria (simulación)
  • En proyectos reales, se conectaría a una base de datos

2. Decoradores de Validación (Validación de Datos)

Garantizan que los datos recibidos cumplan con reglas específicas antes de procesarlos.

Ubicación: ClientesModel.java

@Data@NoArgsConstructor@AllArgsConstructorpublicclassClientesModel {
@NotNull(message = "El nombre no puede ser null")
@NotBlank(message = "El nombre no puede estar vacio")
privateStringnombre;
@NotNull(message = "El correo no puede ser null")
@NotBlank(message = "El correo no puede estar vacio")
privateStringcorreo;
@NotNull(message = "La edad no puede ser null")
@Positive(message = "La edad debe ser mayor a cero")
privateIntegeredad;
}

@NotNull

  • Valida: El valor no puede ser null
  • Tipo: Cualquier tipo de datos
  • Diferencia con @NotBlank: Permite strings vacíos ""

@NotBlank

  • Valida: El string no puede ser null ni estar vacío
  • Trim: Considera espacios en blanco como vacío
  • Tipo: Solo strings

@Positive

  • Valida: El número debe ser mayor que 0
  • Tipo: Números (Integer, Double, BigDecimal, etc.)

Decoradores de Lombok

@Data// Genera: getters, setters, equals(), hashCode(), toString()@NoArgsConstructor// Genera constructor sin argumentos@AllArgsConstructor// Genera constructor con todos los campos

3. Mapeo de Endpoints REST

Verbos HTTP y Decoradores Correspondientes

@RestController@RequestMapping("/api/v1/clientes")
publicclassClientesController {
// GET - Obtener todos los clientes@GetMappingpublicResponseEntity<List<ClientesModel>> listarClientes() { }
// POST - Crear nuevo cliente@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@Valid@RequestBodyClientesModelcliente
) { }
// DELETE - Eliminar cliente por correo@DeleteMapping("{correo}")
publicResponseEntity<String> eliminarCliente(
@PathVariableStringcorreo
) { }
}
DecoratorVerbo HTTPOperaciónDescripción
@GetMappingGETReadObtiene datos sin modificarlos
@PostMappingPOSTCreateCrea un nuevo recurso
@PutMappingPUTUpdateActualiza completamente un recurso
@PatchMappingPATCHPartial UpdateActualiza parcialmente un recurso
@DeleteMappingDELETEDeleteElimina un recurso

Parámetros Importantes

// @RequestBody: Mapea el JSON del request al objeto@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@RequestBodyClientesModelcliente
) { }
// @Valid: Activa la validación de datos@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@Valid@RequestBodyClientesModelcliente
) { }
// @PathVariable: Extrae valores de la ruta@DeleteMapping("{correo}")
publicResponseEntity<String> eliminarCliente(
@PathVariableStringcorreo
) { }

4. HTTP Status Codes (Códigos de Estado)

Los códigos HTTP comunican al cliente el resultado de su request.

Códigos Utilizados en Este Proyecto

CódigoNombreSignificadoCuándo Usar
200OKLa solicitud fue exitosaGET exitoso, DELETE exitoso
201CreatedRecurso creado exitosamentePOST que crea un recurso
400Bad RequestSolicitud inválida o error en validaciónDatos inválidos, errores de negocio
404Not FoundRecurso no encontradoGET de recurso inexistente
500Internal Server ErrorError del servidorExcepciones no controladas

Ejemplo de Uso en el Proyecto

// GET exitoso (200)@GetMappingpublicResponseEntity<List<ClientesModel>> listarClientes() {
returnResponseEntity.status(200).body(clientesService.getClientes());
}
// POST exitoso (201)@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(@Valid@RequestBodyClientesModelcliente) {
returnResponseEntity.status(201).body(clientesService.saveCliente(cliente));
}
// Error en validación (400)// Manejado automáticamente por GlobalExceptionHandler

5. Manejo Centralizado de Excepciones

En lugar de manejar errores en cada endpoint, Spring permite centralizar la gestión en una clase especial.

Ubicación: GlobalExceptionHandler.java

@RestControllerAdvicepublicclassGlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
publicResponseEntity<Map<String, String>> handleValidationErrors(
MethodArgumentNotValidExceptionex
) {
Map<String, String> errores = newHashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errores.put(error.getField(), error.getDefaultMessage())
);
returnResponseEntity.status(HttpStatus.BAD_REQUEST).body(errores);
}
}

¿Cómo funciona?

  1. @RestControllerAdvice: Marca la clase como handler global de excepciones
  2. @ExceptionHandler: Define qué tipo de excepción maneja este método
  3. Cuando alguien envía un cliente sin nombre, Spring:
    • Lanza MethodArgumentNotValidException
    • GlobalExceptionHandler lo captura
    • Retorna un JSON con los errores específicos

Respuesta de Error (ejemplo):

{
"nombre": "El nombre no puede estar vacio",
"edad": "La edad debe ser mayor a cero"
}

🚀 Crear un Nuevo Proyecto con Spring Initializr

Si deseas crear un proyecto similar desde cero, puedes usar Spring Initializr:

Opción 1: Web (Recomendado para principiantes)

  1. Accede a: start.spring.io
  2. Configura:
    • Project: Maven Project
    • Language: Java
    • Spring Boot: 4.0.4 o superior
    • Group: com.duoc
    • Artifact: clientes
    • Java Version: 21
  3. Dependencias (Add Dependencies):
    • Spring Web
    • Spring Boot Validation
    • Lombok
  4. Click "Generate"
  5. Descomprime y abre en tu IDE favorito

Opción 2: Línea de Comandos

curl https://start.spring.io/starter.zip \
-d dependencies=web,validation,lombok \
-d javaVersion=21 \
-d bootVersion=4.0.4 \
-d groupId=com.duoc \
-d artifactId=clientes \
-o clientes.zip
unzip clientes.zip
cd clientes

✅ Paso a Paso para Ejecutar el Proyecto

Paso 1: Clonar o Descargar el Proyecto

cd tu-directorio
# Si es un repositorio git:
git clone <repositorio>cd clientes

Paso 2: Verificar Java

java -version
# Debe mostrar Java 21 o superior

Paso 3: Compilar el Proyecto

mvn clean compile
  • clean: Elimina compilaciones anteriores
  • compile: Compila el código fuente

Paso 4: Ejecutar la Aplicación

mvn spring-boot:run

Salida esperada:

[INFO] Started ClientesApplication in 2.5 seconds

La aplicación estará disponible en: http://localhost:8080


🧪 Pruebas de Endpoints

Herramientas Recomendadas

  • Postman: Interfaz gráfica completa
  • cURL: Línea de comandos (incluido en macOS/Linux)
  • Thunder Client: Extensión de VS Code
  • Insomnia: Similar a Postman

Ejemplos con cURL

1. Listar todos los clientes (GET)

curl -X GET http://localhost:8080/api/v1/clientes

Respuesta esperada (200):

[]

2. Crear un cliente válido (POST)

curl -X POST http://localhost:8080/api/v1/clientes \
-H "Content-Type: application/json" \
-d '{ "nombre": "Juan Pérez", "correo": "juan@example.com", "edad": 25 }'

Respuesta esperada (201):

{
"nombre": "Juan Pérez",
"correo": "juan@example.com",
"edad": 25
}

3. Intentar crear cliente con datos inválidos (POST)

curl -X POST http://localhost:8080/api/v1/clientes \
-H "Content-Type: application/json" \
-d '{ "nombre": "", "correo": "juan@example.com", "edad": -5 }'

Respuesta esperada (400):

{
"nombre": "El nombre no puede estar vacio",
"edad": "La edad debe ser mayor a cero"
}

4. Listar clientes después de crear uno (GET)

curl -X GET http://localhost:8080/api/v1/clientes

Respuesta esperada (200):

[
{
"nombre": "Juan Pérez",
"correo": "juan@example.com",
"edad": 25
}
]

5. Eliminar un cliente (DELETE)

curl -X DELETE http://localhost:8080/api/v1/clientes/juan@example.com

Respuesta esperada (200):

"Cliente eliminado"

📊 Flujo de Datos en la Aplicación

┌─────────────────────────────────────────────────────────────────┐
│ CLIENTE (Postman, cURL) │
└────────────────────────┬──────────────────────────────────────────┘
│ 1. Envía JSON
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesController │
│ - Recibe request HTTP │
│ - Mapea JSON → ClientesModel (@RequestBody) │
│ - Valida datos (@Valid) │
│ - Si hay errores → GlobalExceptionHandler (captura) │
└────────────────────────┬──────────────────────────────────────────┘
│ 2. Si válido, llama servicio
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesService │
│ - Contiene lógica de negocio │
│ - Procesa los datos │
│ - Llama al repositorio │
└────────────────────────┬──────────────────────────────────────────┘
│ 3. Persistencia
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesRepository │
│ - ArrayList (simula base de datos en memoria) │
│ - CRUD: Crear, Leer, Actualizar, Eliminar │
│ - Retorna datos procesados │
└────────────────────────┬──────────────────────────────────────────┘
│ 4. Respuesta
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesController │
│ - ResponseEntity with HTTP Status (200, 201, 400) │
│ - Serializa objeto → JSON │
└────────────────────────┬──────────────────────────────────────────┘
│ 5. Envía JSON
▼
┌─────────────────────────────────────────────────────────────────┐
│ CLIENTE (Recibe respuesta) │
└─────────────────────────────────────────────────────────────────┘

🔄 Patrón de Diseño CSR (Controller-Service-Repository)

CSR es la versión adaptada del patrón MVC específicamente para Spring Boot y APIs REST.

CapaComponenteArchivoResponsabilidad
PresentaciónControllerClientesController.javaMapea rutas HTTP, recibe requests, retorna responses
Lógica de NegocioServiceClientesService.javaContiene la lógica de negocio, orquesta operaciones
DatosRepositoryClientesRepository.javaAcceso a datos, persistencia, operaciones CRUD
DatosModelClientesModel.javaRepresenta la entidad con atributos y validaciones

¿Por qué CSR en lugar de MVC?

En aplicaciones REST con Spring Boot:

  • No hay "View" tradicional (HTML) → La respuesta es JSON
  • La separación de responsabilidades es más clara
  • Service maneja la lógica de negocio (no el Controller)
  • Repository aísla el acceso a datos
  • Facilita testing y mantenimiento

Ventajas del Patrón CSR

✅ Código más limpio y organizado
✅ Fácil de testear (inyección de dependencias)
✅ Escalable (añadir funcionalidades sin afectar otras capas)
✅ Reutilizable (Service puede usarse desde múltiples Controllers)
✅ Mantenible (cambios aislados por capa)


💡 Buenas Prácticas Implementadas

1. Separación de Responsabilidades

Cada clase tiene una único propósito claro:

  • Controller: Mapeo HTTP
  • Service: Lógica de negocio
  • Repository: Acceso a datos

2. Inyección de Dependencias

@AutowiredprivateClientesServiceclientesService;

Facilita: Testing, desacoplamiento, mantenibilidad

3. Validación de Datos a la Entrada

@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(@Valid@RequestBodyClientesModelcliente)

Previene procesamiento de datos inválidos

4. Códigos de Estado HTTP Apropiados

  • 200: Operación exitosa
  • 201: Recurso creado
  • 400: Solicitud inválida
  • 500: Error del servidor

5. Manejo Centralizado de Excepciones

En lugar de try-catch en cada método, usar @RestControllerAdvice

6. Versionamiento de API

/api/v1/clientes permite evitar conflictos al cambiar la API


📖 Ejercicios Propuestos

Nivel 1: Básico

  1. Agregar un nuevo campo telefono a ClientesModel con validación
  2. Crear un endpoint PUT para actualizar cliente
  3. Agregar validación de email usando @Email

Nivel 2: Intermedio

  1. Agregar manejo de excepción personalizada (ej: ClienteNoEncontradoException)
  2. Implementar búsqueda de cliente por correo (GET con parámetro)
  3. Agregar información de error más detallada en respuestas

📝 Recursos Adicionales


🐛 Solución de Problemas Comunes

Error: "Port 8080 already in use"

# Cambiar puerto en application.properties:
server.port=8081

Error: "Field clientesService required a bean of type"

Este error significa que la inyección de dependencias no encontró el bean. Asegurate:

  • La clase ClientesService tiene @Service
  • El controlador tiene @Autowired
  • Spring escanea el paquete correcto

Error: "JSON parse error"

El JSON enviado tiene formato inválido. Verifica:

  • Comillas dobles en propiedades
  • Tipos de datos correctos
  • No hay caracteres de escape faltantes

📄 Licencia

Este proyecto es de código abierto y está disponible bajo la licencia MIT, diseñado para propósitos educativos.


Última actualización: 28 de marzo de 2026

About

Proyecto educativo de Fullstack I para estudiantes de segundo año de Ingeniería en Informática: API REST en Spring Boot para gestión de clientes, aplicando validaciones, arquitectura por capas y buenas prácticas HTTP.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - raicerk/fullstack-i-clientes: Proyecto educativo de Fullstack I para estudiantes de segundo año de Ingeniería en Informática: API REST en Spring Boot para gestión de clientes, aplicando validaciones, arquitectura por capas y buenas prácticas HTTP. · GitHub
Skip to content

Repository files navigation

Proyecto: Gestión de Clientes con Spring Boot

Un proyecto educativo diseñado para estudiantes de segundo año de Ingeniería en Computación que enseña los principios fundamentales de Spring Framework, decoradores básicos y de validación, buenas prácticas de APIs REST con HTTP status codes, y manejo centralizado de errores.

Este ejercicio corresponde al ramo Fullstack I para estudiantes de Ingeniería en Informática de DUOC UC.

📚 Objetivos de Aprendizaje

  • Comprender la arquitectura de una aplicación Spring Boot
  • Dominar decoradores (anotaciones) básicas y de validación
  • Implementar APIs REST siguiendo buenas prácticas
  • Usar códigos de estado HTTP apropiados
  • Implementar manejo centralizado de excepciones
  • Aplicar inyección de dependencias
  • Utilizar el patrón de diseño CSR (Controller-Service-Repository): adaptación de MVC para Spring Boot

🔧 Requisitos Previos

  • Java 21 o superior
  • Maven 3.6+ para gestión de dependencias
  • IDE recomendado: IntelliJ IDEA, Visual Studio Code o Eclipse
  • Conocimientos básicos de Java y POO
  • Conceptos básicos de APIs REST y HTTP

📦 Dependencias Principales

<!-- Spring Boot Starter Web (REST Controllers y servidores web) -->
spring-boot-starter-web
<!-- Spring Boot Starter Validation (Validación de datos) -->
spring-boot-starter-validation
<!-- Lombok (Generación automática de getters, setters, etc.) -->
lombok
<!-- Spring Boot Starter Test (Pruebas unitarias) -->
spring-boot-starter-test

🏗️ Estructura del Proyecto

clientes/
├── src/
│ ├── main/
│ │ ├── java/com/duoc/clientes/
│ │ │ ├── ClientesApplication.java # Punto de entrada
│ │ │ ├── controller/
│ │ │ │ └── ClientesController.java # Endpoints REST
│ │ │ ├── model/
│ │ │ │ └── ClientesModel.java # Entidad con validaciones
│ │ │ ├── service/
│ │ │ │ └── ClientesService.java # Lógica de negocio
│ │ │ ├── repository/
│ │ │ │ └── ClientesRepository.java # Acceso a datos
│ │ │ └── exception/
│ │ │ └── GlobalExceptionHandler.java # Manejo de errores
│ │ └── resources/
│ │ └── application.properties # Configuración
│ └── test/
│ └── ClientesApplicationTests.java # Pruebas
└── pom.xml # Configuración Maven

🎯 Conceptos Clave

1. Decoradores (Anotaciones) Básicos de Spring

Las anotaciones son etiquetas especiales que proporcionan metadatos sobre el programa, no affecting directly the operation of the code but providing information to the framework.

@SpringBootApplication

Ubicación: ClientesApplication.java

@SpringBootApplicationpublicclassClientesApplication {
publicstaticvoidmain(String[] args) {
SpringApplication.run(ClientesApplication.class, args);
}
}

¿Qué hace? Combina tres anotaciones en una:

  • @Configuration: Marca la clase como fuente de definiciones de beans
  • @EnableAutoConfiguration: Permite que Spring Boot configure automáticamente la aplicación basándose en las dependencias
  • @ComponentScan: Escanea el paquete actual y subpaquetes buscando componentes anotados

@RestController

Ubicación: ClientesController.java

@RestController@RequestMapping("/api/v1/clientes")
publicclassClientesController {
// ...
}

¿Qué hace?

  • Marca la clase como controlador REST
  • Equivalente a @Controller + @ResponseBody
  • Los métodos retornan datos serializados (JSON) automáticamente
  • @RequestMapping("/api/v1/clientes"): Define la ruta base para todos los endpoints

@Autowired

Ubicación: ClientesController.java

@AutowiredprivateClientesServiceclientesService;

¿Qué hace?

  • Inyección de Dependencias: Spring inyecta automáticamente una instancia de ClientesService
  • No necesitas crear la instancia manualmente con new
  • Spring gestiona el ciclo de vida del objeto
  • Promueve el desacoplamiento y facilita las pruebas

@Service

Ubicación: ClientesService.java

@ServicepublicclassClientesService {
// ...
}

¿Qué hace?

  • Marca la clase como un bean de servicio (componente del negocio)
  • Contiene la lógica empresarial
  • Spring la detecta automáticamente y la registra como bean
  • Se puede inyectar en otros componentes

@Repository

Ubicación: ClientesRepository.java

@RepositorypublicclassClientesRepository {
// ...
}

¿Qué hace?

  • Marca la clase como un repositorio (capa de acceso a datos)
  • Responsable de CRUD (Create, Read, Update, Delete)
  • En este proyecto, usa un ArrayList en memoria (simulación)
  • En proyectos reales, se conectaría a una base de datos

2. Decoradores de Validación (Validación de Datos)

Garantizan que los datos recibidos cumplan con reglas específicas antes de procesarlos.

Ubicación: ClientesModel.java

@Data@NoArgsConstructor@AllArgsConstructorpublicclassClientesModel {
@NotNull(message = "El nombre no puede ser null")
@NotBlank(message = "El nombre no puede estar vacio")
privateStringnombre;
@NotNull(message = "El correo no puede ser null")
@NotBlank(message = "El correo no puede estar vacio")
privateStringcorreo;
@NotNull(message = "La edad no puede ser null")
@Positive(message = "La edad debe ser mayor a cero")
privateIntegeredad;
}

@NotNull

  • Valida: El valor no puede ser null
  • Tipo: Cualquier tipo de datos
  • Diferencia con @NotBlank: Permite strings vacíos ""

@NotBlank

  • Valida: El string no puede ser null ni estar vacío
  • Trim: Considera espacios en blanco como vacío
  • Tipo: Solo strings

@Positive

  • Valida: El número debe ser mayor que 0
  • Tipo: Números (Integer, Double, BigDecimal, etc.)

Decoradores de Lombok

@Data// Genera: getters, setters, equals(), hashCode(), toString()@NoArgsConstructor// Genera constructor sin argumentos@AllArgsConstructor// Genera constructor con todos los campos

3. Mapeo de Endpoints REST

Verbos HTTP y Decoradores Correspondientes

@RestController@RequestMapping("/api/v1/clientes")
publicclassClientesController {
// GET - Obtener todos los clientes@GetMappingpublicResponseEntity<List<ClientesModel>> listarClientes() { }
// POST - Crear nuevo cliente@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@Valid@RequestBodyClientesModelcliente
) { }
// DELETE - Eliminar cliente por correo@DeleteMapping("{correo}")
publicResponseEntity<String> eliminarCliente(
@PathVariableStringcorreo
) { }
}
DecoratorVerbo HTTPOperaciónDescripción
@GetMappingGETReadObtiene datos sin modificarlos
@PostMappingPOSTCreateCrea un nuevo recurso
@PutMappingPUTUpdateActualiza completamente un recurso
@PatchMappingPATCHPartial UpdateActualiza parcialmente un recurso
@DeleteMappingDELETEDeleteElimina un recurso

Parámetros Importantes

// @RequestBody: Mapea el JSON del request al objeto@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@RequestBodyClientesModelcliente
) { }
// @Valid: Activa la validación de datos@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@Valid@RequestBodyClientesModelcliente
) { }
// @PathVariable: Extrae valores de la ruta@DeleteMapping("{correo}")
publicResponseEntity<String> eliminarCliente(
@PathVariableStringcorreo
) { }

4. HTTP Status Codes (Códigos de Estado)

Los códigos HTTP comunican al cliente el resultado de su request.

Códigos Utilizados en Este Proyecto

CódigoNombreSignificadoCuándo Usar
200OKLa solicitud fue exitosaGET exitoso, DELETE exitoso
201CreatedRecurso creado exitosamentePOST que crea un recurso
400Bad RequestSolicitud inválida o error en validaciónDatos inválidos, errores de negocio
404Not FoundRecurso no encontradoGET de recurso inexistente
500Internal Server ErrorError del servidorExcepciones no controladas

Ejemplo de Uso en el Proyecto

// GET exitoso (200)@GetMappingpublicResponseEntity<List<ClientesModel>> listarClientes() {
returnResponseEntity.status(200).body(clientesService.getClientes());
}
// POST exitoso (201)@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(@Valid@RequestBodyClientesModelcliente) {
returnResponseEntity.status(201).body(clientesService.saveCliente(cliente));
}
// Error en validación (400)// Manejado automáticamente por GlobalExceptionHandler

5. Manejo Centralizado de Excepciones

En lugar de manejar errores en cada endpoint, Spring permite centralizar la gestión en una clase especial.

Ubicación: GlobalExceptionHandler.java

@RestControllerAdvicepublicclassGlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
publicResponseEntity<Map<String, String>> handleValidationErrors(
MethodArgumentNotValidExceptionex
) {
Map<String, String> errores = newHashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errores.put(error.getField(), error.getDefaultMessage())
);
returnResponseEntity.status(HttpStatus.BAD_REQUEST).body(errores);
}
}

¿Cómo funciona?

  1. @RestControllerAdvice: Marca la clase como handler global de excepciones
  2. @ExceptionHandler: Define qué tipo de excepción maneja este método
  3. Cuando alguien envía un cliente sin nombre, Spring:
    • Lanza MethodArgumentNotValidException
    • GlobalExceptionHandler lo captura
    • Retorna un JSON con los errores específicos

Respuesta de Error (ejemplo):

{
"nombre": "El nombre no puede estar vacio",
"edad": "La edad debe ser mayor a cero"
}

🚀 Crear un Nuevo Proyecto con Spring Initializr

Si deseas crear un proyecto similar desde cero, puedes usar Spring Initializr:

Opción 1: Web (Recomendado para principiantes)

  1. Accede a: start.spring.io
  2. Configura:
    • Project: Maven Project
    • Language: Java
    • Spring Boot: 4.0.4 o superior
    • Group: com.duoc
    • Artifact: clientes
    • Java Version: 21
  3. Dependencias (Add Dependencies):
    • Spring Web
    • Spring Boot Validation
    • Lombok
  4. Click "Generate"
  5. Descomprime y abre en tu IDE favorito

Opción 2: Línea de Comandos

curl https://start.spring.io/starter.zip \
-d dependencies=web,validation,lombok \
-d javaVersion=21 \
-d bootVersion=4.0.4 \
-d groupId=com.duoc \
-d artifactId=clientes \
-o clientes.zip
unzip clientes.zip
cd clientes

✅ Paso a Paso para Ejecutar el Proyecto

Paso 1: Clonar o Descargar el Proyecto

cd tu-directorio
# Si es un repositorio git:
git clone <repositorio>cd clientes

Paso 2: Verificar Java

java -version
# Debe mostrar Java 21 o superior

Paso 3: Compilar el Proyecto

mvn clean compile
  • clean: Elimina compilaciones anteriores
  • compile: Compila el código fuente

Paso 4: Ejecutar la Aplicación

mvn spring-boot:run

Salida esperada:

[INFO] Started ClientesApplication in 2.5 seconds

La aplicación estará disponible en: http://localhost:8080


🧪 Pruebas de Endpoints

Herramientas Recomendadas

  • Postman: Interfaz gráfica completa
  • cURL: Línea de comandos (incluido en macOS/Linux)
  • Thunder Client: Extensión de VS Code
  • Insomnia: Similar a Postman

Ejemplos con cURL

1. Listar todos los clientes (GET)

curl -X GET http://localhost:8080/api/v1/clientes

Respuesta esperada (200):

[]

2. Crear un cliente válido (POST)

curl -X POST http://localhost:8080/api/v1/clientes \
-H "Content-Type: application/json" \
-d '{ "nombre": "Juan Pérez", "correo": "juan@example.com", "edad": 25 }'

Respuesta esperada (201):

{
"nombre": "Juan Pérez",
"correo": "juan@example.com",
"edad": 25
}

3. Intentar crear cliente con datos inválidos (POST)

curl -X POST http://localhost:8080/api/v1/clientes \
-H "Content-Type: application/json" \
-d '{ "nombre": "", "correo": "juan@example.com", "edad": -5 }'

Respuesta esperada (400):

{
"nombre": "El nombre no puede estar vacio",
"edad": "La edad debe ser mayor a cero"
}

4. Listar clientes después de crear uno (GET)

curl -X GET http://localhost:8080/api/v1/clientes

Respuesta esperada (200):

[
{
"nombre": "Juan Pérez",
"correo": "juan@example.com",
"edad": 25
}
]

5. Eliminar un cliente (DELETE)

curl -X DELETE http://localhost:8080/api/v1/clientes/juan@example.com

Respuesta esperada (200):

"Cliente eliminado"

📊 Flujo de Datos en la Aplicación

┌─────────────────────────────────────────────────────────────────┐
│ CLIENTE (Postman, cURL) │
└────────────────────────┬──────────────────────────────────────────┘
│ 1. Envía JSON
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesController │
│ - Recibe request HTTP │
│ - Mapea JSON → ClientesModel (@RequestBody) │
│ - Valida datos (@Valid) │
│ - Si hay errores → GlobalExceptionHandler (captura) │
└────────────────────────┬──────────────────────────────────────────┘
│ 2. Si válido, llama servicio
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesService │
│ - Contiene lógica de negocio │
│ - Procesa los datos │
│ - Llama al repositorio │
└────────────────────────┬──────────────────────────────────────────┘
│ 3. Persistencia
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesRepository │
│ - ArrayList (simula base de datos en memoria) │
│ - CRUD: Crear, Leer, Actualizar, Eliminar │
│ - Retorna datos procesados │
└────────────────────────┬──────────────────────────────────────────┘
│ 4. Respuesta
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesController │
│ - ResponseEntity with HTTP Status (200, 201, 400) │
│ - Serializa objeto → JSON │
└────────────────────────┬──────────────────────────────────────────┘
│ 5. Envía JSON
▼
┌─────────────────────────────────────────────────────────────────┐
│ CLIENTE (Recibe respuesta) │
└─────────────────────────────────────────────────────────────────┘

🔄 Patrón de Diseño CSR (Controller-Service-Repository)

CSR es la versión adaptada del patrón MVC específicamente para Spring Boot y APIs REST.

CapaComponenteArchivoResponsabilidad
PresentaciónControllerClientesController.javaMapea rutas HTTP, recibe requests, retorna responses
Lógica de NegocioServiceClientesService.javaContiene la lógica de negocio, orquesta operaciones
DatosRepositoryClientesRepository.javaAcceso a datos, persistencia, operaciones CRUD
DatosModelClientesModel.javaRepresenta la entidad con atributos y validaciones

¿Por qué CSR en lugar de MVC?

En aplicaciones REST con Spring Boot:

  • No hay "View" tradicional (HTML) → La respuesta es JSON
  • La separación de responsabilidades es más clara
  • Service maneja la lógica de negocio (no el Controller)
  • Repository aísla el acceso a datos
  • Facilita testing y mantenimiento

Ventajas del Patrón CSR

✅ Código más limpio y organizado
✅ Fácil de testear (inyección de dependencias)
✅ Escalable (añadir funcionalidades sin afectar otras capas)
✅ Reutilizable (Service puede usarse desde múltiples Controllers)
✅ Mantenible (cambios aislados por capa)


💡 Buenas Prácticas Implementadas

1. Separación de Responsabilidades

Cada clase tiene una único propósito claro:

  • Controller: Mapeo HTTP
  • Service: Lógica de negocio
  • Repository: Acceso a datos

2. Inyección de Dependencias

@AutowiredprivateClientesServiceclientesService;

Facilita: Testing, desacoplamiento, mantenibilidad

3. Validación de Datos a la Entrada

@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(@Valid@RequestBodyClientesModelcliente)

Previene procesamiento de datos inválidos

4. Códigos de Estado HTTP Apropiados

  • 200: Operación exitosa
  • 201: Recurso creado
  • 400: Solicitud inválida
  • 500: Error del servidor

5. Manejo Centralizado de Excepciones

En lugar de try-catch en cada método, usar @RestControllerAdvice

6. Versionamiento de API

/api/v1/clientes permite evitar conflictos al cambiar la API


📖 Ejercicios Propuestos

Nivel 1: Básico

  1. Agregar un nuevo campo telefono a ClientesModel con validación
  2. Crear un endpoint PUT para actualizar cliente
  3. Agregar validación de email usando @Email

Nivel 2: Intermedio

  1. Agregar manejo de excepción personalizada (ej: ClienteNoEncontradoException)
  2. Implementar búsqueda de cliente por correo (GET con parámetro)
  3. Agregar información de error más detallada en respuestas

📝 Recursos Adicionales


🐛 Solución de Problemas Comunes

Error: "Port 8080 already in use"

# Cambiar puerto en application.properties:
server.port=8081

Error: "Field clientesService required a bean of type"

Este error significa que la inyección de dependencias no encontró el bean. Asegurate:

  • La clase ClientesService tiene @Service
  • El controlador tiene @Autowired
  • Spring escanea el paquete correcto

Error: "JSON parse error"

El JSON enviado tiene formato inválido. Verifica:

  • Comillas dobles en propiedades
  • Tipos de datos correctos
  • No hay caracteres de escape faltantes

📄 Licencia

Este proyecto es de código abierto y está disponible bajo la licencia MIT, diseñado para propósitos educativos.


Última actualización: 28 de marzo de 2026

About

Proyecto educativo de Fullstack I para estudiantes de segundo año de Ingeniería en Informática: API REST en Spring Boot para gestión de clientes, aplicando validaciones, arquitectura por capas y buenas prácticas HTTP.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - raicerk/fullstack-i-clientes: Proyecto educativo de Fullstack I para estudiantes de segundo año de Ingeniería en Informática: API REST en Spring Boot para gestión de clientes, aplicando validaciones, arquitectura por capas y buenas prácticas HTTP. · GitHub
Skip to content

Repository files navigation

Proyecto: Gestión de Clientes con Spring Boot

Un proyecto educativo diseñado para estudiantes de segundo año de Ingeniería en Computación que enseña los principios fundamentales de Spring Framework, decoradores básicos y de validación, buenas prácticas de APIs REST con HTTP status codes, y manejo centralizado de errores.

Este ejercicio corresponde al ramo Fullstack I para estudiantes de Ingeniería en Informática de DUOC UC.

📚 Objetivos de Aprendizaje

  • Comprender la arquitectura de una aplicación Spring Boot
  • Dominar decoradores (anotaciones) básicas y de validación
  • Implementar APIs REST siguiendo buenas prácticas
  • Usar códigos de estado HTTP apropiados
  • Implementar manejo centralizado de excepciones
  • Aplicar inyección de dependencias
  • Utilizar el patrón de diseño CSR (Controller-Service-Repository): adaptación de MVC para Spring Boot

🔧 Requisitos Previos

  • Java 21 o superior
  • Maven 3.6+ para gestión de dependencias
  • IDE recomendado: IntelliJ IDEA, Visual Studio Code o Eclipse
  • Conocimientos básicos de Java y POO
  • Conceptos básicos de APIs REST y HTTP

📦 Dependencias Principales

<!-- Spring Boot Starter Web (REST Controllers y servidores web) -->
spring-boot-starter-web
<!-- Spring Boot Starter Validation (Validación de datos) -->
spring-boot-starter-validation
<!-- Lombok (Generación automática de getters, setters, etc.) -->
lombok
<!-- Spring Boot Starter Test (Pruebas unitarias) -->
spring-boot-starter-test

🏗️ Estructura del Proyecto

clientes/
├── src/
│ ├── main/
│ │ ├── java/com/duoc/clientes/
│ │ │ ├── ClientesApplication.java # Punto de entrada
│ │ │ ├── controller/
│ │ │ │ └── ClientesController.java # Endpoints REST
│ │ │ ├── model/
│ │ │ │ └── ClientesModel.java # Entidad con validaciones
│ │ │ ├── service/
│ │ │ │ └── ClientesService.java # Lógica de negocio
│ │ │ ├── repository/
│ │ │ │ └── ClientesRepository.java # Acceso a datos
│ │ │ └── exception/
│ │ │ └── GlobalExceptionHandler.java # Manejo de errores
│ │ └── resources/
│ │ └── application.properties # Configuración
│ └── test/
│ └── ClientesApplicationTests.java # Pruebas
└── pom.xml # Configuración Maven

🎯 Conceptos Clave

1. Decoradores (Anotaciones) Básicos de Spring

Las anotaciones son etiquetas especiales que proporcionan metadatos sobre el programa, no affecting directly the operation of the code but providing information to the framework.

@SpringBootApplication

Ubicación: ClientesApplication.java

@SpringBootApplicationpublicclassClientesApplication {
publicstaticvoidmain(String[] args) {
SpringApplication.run(ClientesApplication.class, args);
}
}

¿Qué hace? Combina tres anotaciones en una:

  • @Configuration: Marca la clase como fuente de definiciones de beans
  • @EnableAutoConfiguration: Permite que Spring Boot configure automáticamente la aplicación basándose en las dependencias
  • @ComponentScan: Escanea el paquete actual y subpaquetes buscando componentes anotados

@RestController

Ubicación: ClientesController.java

@RestController@RequestMapping("/api/v1/clientes")
publicclassClientesController {
// ...
}

¿Qué hace?

  • Marca la clase como controlador REST
  • Equivalente a @Controller + @ResponseBody
  • Los métodos retornan datos serializados (JSON) automáticamente
  • @RequestMapping("/api/v1/clientes"): Define la ruta base para todos los endpoints

@Autowired

Ubicación: ClientesController.java

@AutowiredprivateClientesServiceclientesService;

¿Qué hace?

  • Inyección de Dependencias: Spring inyecta automáticamente una instancia de ClientesService
  • No necesitas crear la instancia manualmente con new
  • Spring gestiona el ciclo de vida del objeto
  • Promueve el desacoplamiento y facilita las pruebas

@Service

Ubicación: ClientesService.java

@ServicepublicclassClientesService {
// ...
}

¿Qué hace?

  • Marca la clase como un bean de servicio (componente del negocio)
  • Contiene la lógica empresarial
  • Spring la detecta automáticamente y la registra como bean
  • Se puede inyectar en otros componentes

@Repository

Ubicación: ClientesRepository.java

@RepositorypublicclassClientesRepository {
// ...
}

¿Qué hace?

  • Marca la clase como un repositorio (capa de acceso a datos)
  • Responsable de CRUD (Create, Read, Update, Delete)
  • En este proyecto, usa un ArrayList en memoria (simulación)
  • En proyectos reales, se conectaría a una base de datos

2. Decoradores de Validación (Validación de Datos)

Garantizan que los datos recibidos cumplan con reglas específicas antes de procesarlos.

Ubicación: ClientesModel.java

@Data@NoArgsConstructor@AllArgsConstructorpublicclassClientesModel {
@NotNull(message = "El nombre no puede ser null")
@NotBlank(message = "El nombre no puede estar vacio")
privateStringnombre;
@NotNull(message = "El correo no puede ser null")
@NotBlank(message = "El correo no puede estar vacio")
privateStringcorreo;
@NotNull(message = "La edad no puede ser null")
@Positive(message = "La edad debe ser mayor a cero")
privateIntegeredad;
}

@NotNull

  • Valida: El valor no puede ser null
  • Tipo: Cualquier tipo de datos
  • Diferencia con @NotBlank: Permite strings vacíos ""

@NotBlank

  • Valida: El string no puede ser null ni estar vacío
  • Trim: Considera espacios en blanco como vacío
  • Tipo: Solo strings

@Positive

  • Valida: El número debe ser mayor que 0
  • Tipo: Números (Integer, Double, BigDecimal, etc.)

Decoradores de Lombok

@Data// Genera: getters, setters, equals(), hashCode(), toString()@NoArgsConstructor// Genera constructor sin argumentos@AllArgsConstructor// Genera constructor con todos los campos

3. Mapeo de Endpoints REST

Verbos HTTP y Decoradores Correspondientes

@RestController@RequestMapping("/api/v1/clientes")
publicclassClientesController {
// GET - Obtener todos los clientes@GetMappingpublicResponseEntity<List<ClientesModel>> listarClientes() { }
// POST - Crear nuevo cliente@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@Valid@RequestBodyClientesModelcliente
) { }
// DELETE - Eliminar cliente por correo@DeleteMapping("{correo}")
publicResponseEntity<String> eliminarCliente(
@PathVariableStringcorreo
) { }
}
DecoratorVerbo HTTPOperaciónDescripción
@GetMappingGETReadObtiene datos sin modificarlos
@PostMappingPOSTCreateCrea un nuevo recurso
@PutMappingPUTUpdateActualiza completamente un recurso
@PatchMappingPATCHPartial UpdateActualiza parcialmente un recurso
@DeleteMappingDELETEDeleteElimina un recurso

Parámetros Importantes

// @RequestBody: Mapea el JSON del request al objeto@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@RequestBodyClientesModelcliente
) { }
// @Valid: Activa la validación de datos@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@Valid@RequestBodyClientesModelcliente
) { }
// @PathVariable: Extrae valores de la ruta@DeleteMapping("{correo}")
publicResponseEntity<String> eliminarCliente(
@PathVariableStringcorreo
) { }

4. HTTP Status Codes (Códigos de Estado)

Los códigos HTTP comunican al cliente el resultado de su request.

Códigos Utilizados en Este Proyecto

CódigoNombreSignificadoCuándo Usar
200OKLa solicitud fue exitosaGET exitoso, DELETE exitoso
201CreatedRecurso creado exitosamentePOST que crea un recurso
400Bad RequestSolicitud inválida o error en validaciónDatos inválidos, errores de negocio
404Not FoundRecurso no encontradoGET de recurso inexistente
500Internal Server ErrorError del servidorExcepciones no controladas

Ejemplo de Uso en el Proyecto

// GET exitoso (200)@GetMappingpublicResponseEntity<List<ClientesModel>> listarClientes() {
returnResponseEntity.status(200).body(clientesService.getClientes());
}
// POST exitoso (201)@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(@Valid@RequestBodyClientesModelcliente) {
returnResponseEntity.status(201).body(clientesService.saveCliente(cliente));
}
// Error en validación (400)// Manejado automáticamente por GlobalExceptionHandler

5. Manejo Centralizado de Excepciones

En lugar de manejar errores en cada endpoint, Spring permite centralizar la gestión en una clase especial.

Ubicación: GlobalExceptionHandler.java

@RestControllerAdvicepublicclassGlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
publicResponseEntity<Map<String, String>> handleValidationErrors(
MethodArgumentNotValidExceptionex
) {
Map<String, String> errores = newHashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errores.put(error.getField(), error.getDefaultMessage())
);
returnResponseEntity.status(HttpStatus.BAD_REQUEST).body(errores);
}
}

¿Cómo funciona?

  1. @RestControllerAdvice: Marca la clase como handler global de excepciones
  2. @ExceptionHandler: Define qué tipo de excepción maneja este método
  3. Cuando alguien envía un cliente sin nombre, Spring:
    • Lanza MethodArgumentNotValidException
    • GlobalExceptionHandler lo captura
    • Retorna un JSON con los errores específicos

Respuesta de Error (ejemplo):

{
"nombre": "El nombre no puede estar vacio",
"edad": "La edad debe ser mayor a cero"
}

🚀 Crear un Nuevo Proyecto con Spring Initializr

Si deseas crear un proyecto similar desde cero, puedes usar Spring Initializr:

Opción 1: Web (Recomendado para principiantes)

  1. Accede a: start.spring.io
  2. Configura:
    • Project: Maven Project
    • Language: Java
    • Spring Boot: 4.0.4 o superior
    • Group: com.duoc
    • Artifact: clientes
    • Java Version: 21
  3. Dependencias (Add Dependencies):
    • Spring Web
    • Spring Boot Validation
    • Lombok
  4. Click "Generate"
  5. Descomprime y abre en tu IDE favorito

Opción 2: Línea de Comandos

curl https://start.spring.io/starter.zip \
-d dependencies=web,validation,lombok \
-d javaVersion=21 \
-d bootVersion=4.0.4 \
-d groupId=com.duoc \
-d artifactId=clientes \
-o clientes.zip
unzip clientes.zip
cd clientes

✅ Paso a Paso para Ejecutar el Proyecto

Paso 1: Clonar o Descargar el Proyecto

cd tu-directorio
# Si es un repositorio git:
git clone <repositorio>cd clientes

Paso 2: Verificar Java

java -version
# Debe mostrar Java 21 o superior

Paso 3: Compilar el Proyecto

mvn clean compile
  • clean: Elimina compilaciones anteriores
  • compile: Compila el código fuente

Paso 4: Ejecutar la Aplicación

mvn spring-boot:run

Salida esperada:

[INFO] Started ClientesApplication in 2.5 seconds

La aplicación estará disponible en: http://localhost:8080


🧪 Pruebas de Endpoints

Herramientas Recomendadas

  • Postman: Interfaz gráfica completa
  • cURL: Línea de comandos (incluido en macOS/Linux)
  • Thunder Client: Extensión de VS Code
  • Insomnia: Similar a Postman

Ejemplos con cURL

1. Listar todos los clientes (GET)

curl -X GET http://localhost:8080/api/v1/clientes

Respuesta esperada (200):

[]

2. Crear un cliente válido (POST)

curl -X POST http://localhost:8080/api/v1/clientes \
-H "Content-Type: application/json" \
-d '{ "nombre": "Juan Pérez", "correo": "juan@example.com", "edad": 25 }'

Respuesta esperada (201):

{
"nombre": "Juan Pérez",
"correo": "juan@example.com",
"edad": 25
}

3. Intentar crear cliente con datos inválidos (POST)

curl -X POST http://localhost:8080/api/v1/clientes \
-H "Content-Type: application/json" \
-d '{ "nombre": "", "correo": "juan@example.com", "edad": -5 }'

Respuesta esperada (400):

{
"nombre": "El nombre no puede estar vacio",
"edad": "La edad debe ser mayor a cero"
}

4. Listar clientes después de crear uno (GET)

curl -X GET http://localhost:8080/api/v1/clientes

Respuesta esperada (200):

[
{
"nombre": "Juan Pérez",
"correo": "juan@example.com",
"edad": 25
}
]

5. Eliminar un cliente (DELETE)

curl -X DELETE http://localhost:8080/api/v1/clientes/juan@example.com

Respuesta esperada (200):

"Cliente eliminado"

📊 Flujo de Datos en la Aplicación

┌─────────────────────────────────────────────────────────────────┐
│ CLIENTE (Postman, cURL) │
└────────────────────────┬──────────────────────────────────────────┘
│ 1. Envía JSON
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesController │
│ - Recibe request HTTP │
│ - Mapea JSON → ClientesModel (@RequestBody) │
│ - Valida datos (@Valid) │
│ - Si hay errores → GlobalExceptionHandler (captura) │
└────────────────────────┬──────────────────────────────────────────┘
│ 2. Si válido, llama servicio
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesService │
│ - Contiene lógica de negocio │
│ - Procesa los datos │
│ - Llama al repositorio │
└────────────────────────┬──────────────────────────────────────────┘
│ 3. Persistencia
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesRepository │
│ - ArrayList (simula base de datos en memoria) │
│ - CRUD: Crear, Leer, Actualizar, Eliminar │
│ - Retorna datos procesados │
└────────────────────────┬──────────────────────────────────────────┘
│ 4. Respuesta
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesController │
│ - ResponseEntity with HTTP Status (200, 201, 400) │
│ - Serializa objeto → JSON │
└────────────────────────┬──────────────────────────────────────────┘
│ 5. Envía JSON
▼
┌─────────────────────────────────────────────────────────────────┐
│ CLIENTE (Recibe respuesta) │
└─────────────────────────────────────────────────────────────────┘

🔄 Patrón de Diseño CSR (Controller-Service-Repository)

CSR es la versión adaptada del patrón MVC específicamente para Spring Boot y APIs REST.

CapaComponenteArchivoResponsabilidad
PresentaciónControllerClientesController.javaMapea rutas HTTP, recibe requests, retorna responses
Lógica de NegocioServiceClientesService.javaContiene la lógica de negocio, orquesta operaciones
DatosRepositoryClientesRepository.javaAcceso a datos, persistencia, operaciones CRUD
DatosModelClientesModel.javaRepresenta la entidad con atributos y validaciones

¿Por qué CSR en lugar de MVC?

En aplicaciones REST con Spring Boot:

  • No hay "View" tradicional (HTML) → La respuesta es JSON
  • La separación de responsabilidades es más clara
  • Service maneja la lógica de negocio (no el Controller)
  • Repository aísla el acceso a datos
  • Facilita testing y mantenimiento

Ventajas del Patrón CSR

✅ Código más limpio y organizado
✅ Fácil de testear (inyección de dependencias)
✅ Escalable (añadir funcionalidades sin afectar otras capas)
✅ Reutilizable (Service puede usarse desde múltiples Controllers)
✅ Mantenible (cambios aislados por capa)


💡 Buenas Prácticas Implementadas

1. Separación de Responsabilidades

Cada clase tiene una único propósito claro:

  • Controller: Mapeo HTTP
  • Service: Lógica de negocio
  • Repository: Acceso a datos

2. Inyección de Dependencias

@AutowiredprivateClientesServiceclientesService;

Facilita: Testing, desacoplamiento, mantenibilidad

3. Validación de Datos a la Entrada

@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(@Valid@RequestBodyClientesModelcliente)

Previene procesamiento de datos inválidos

4. Códigos de Estado HTTP Apropiados

  • 200: Operación exitosa
  • 201: Recurso creado
  • 400: Solicitud inválida
  • 500: Error del servidor

5. Manejo Centralizado de Excepciones

En lugar de try-catch en cada método, usar @RestControllerAdvice

6. Versionamiento de API

/api/v1/clientes permite evitar conflictos al cambiar la API


📖 Ejercicios Propuestos

Nivel 1: Básico

  1. Agregar un nuevo campo telefono a ClientesModel con validación
  2. Crear un endpoint PUT para actualizar cliente
  3. Agregar validación de email usando @Email

Nivel 2: Intermedio

  1. Agregar manejo de excepción personalizada (ej: ClienteNoEncontradoException)
  2. Implementar búsqueda de cliente por correo (GET con parámetro)
  3. Agregar información de error más detallada en respuestas

📝 Recursos Adicionales


🐛 Solución de Problemas Comunes

Error: "Port 8080 already in use"

# Cambiar puerto en application.properties:
server.port=8081

Error: "Field clientesService required a bean of type"

Este error significa que la inyección de dependencias no encontró el bean. Asegurate:

  • La clase ClientesService tiene @Service
  • El controlador tiene @Autowired
  • Spring escanea el paquete correcto

Error: "JSON parse error"

El JSON enviado tiene formato inválido. Verifica:

  • Comillas dobles en propiedades
  • Tipos de datos correctos
  • No hay caracteres de escape faltantes

📄 Licencia

Este proyecto es de código abierto y está disponible bajo la licencia MIT, diseñado para propósitos educativos.


Última actualización: 28 de marzo de 2026

About

Proyecto educativo de Fullstack I para estudiantes de segundo año de Ingeniería en Informática: API REST en Spring Boot para gestión de clientes, aplicando validaciones, arquitectura por capas y buenas prácticas HTTP.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - raicerk/fullstack-i-clientes: Proyecto educativo de Fullstack I para estudiantes de segundo año de Ingeniería en Informática: API REST en Spring Boot para gestión de clientes, aplicando validaciones, arquitectura por capas y buenas prácticas HTTP. · GitHub
Skip to content

Repository files navigation

Proyecto: Gestión de Clientes con Spring Boot

Un proyecto educativo diseñado para estudiantes de segundo año de Ingeniería en Computación que enseña los principios fundamentales de Spring Framework, decoradores básicos y de validación, buenas prácticas de APIs REST con HTTP status codes, y manejo centralizado de errores.

Este ejercicio corresponde al ramo Fullstack I para estudiantes de Ingeniería en Informática de DUOC UC.

📚 Objetivos de Aprendizaje

  • Comprender la arquitectura de una aplicación Spring Boot
  • Dominar decoradores (anotaciones) básicas y de validación
  • Implementar APIs REST siguiendo buenas prácticas
  • Usar códigos de estado HTTP apropiados
  • Implementar manejo centralizado de excepciones
  • Aplicar inyección de dependencias
  • Utilizar el patrón de diseño CSR (Controller-Service-Repository): adaptación de MVC para Spring Boot

🔧 Requisitos Previos

  • Java 21 o superior
  • Maven 3.6+ para gestión de dependencias
  • IDE recomendado: IntelliJ IDEA, Visual Studio Code o Eclipse
  • Conocimientos básicos de Java y POO
  • Conceptos básicos de APIs REST y HTTP

📦 Dependencias Principales

<!-- Spring Boot Starter Web (REST Controllers y servidores web) -->
spring-boot-starter-web
<!-- Spring Boot Starter Validation (Validación de datos) -->
spring-boot-starter-validation
<!-- Lombok (Generación automática de getters, setters, etc.) -->
lombok
<!-- Spring Boot Starter Test (Pruebas unitarias) -->
spring-boot-starter-test

🏗️ Estructura del Proyecto

clientes/
├── src/
│ ├── main/
│ │ ├── java/com/duoc/clientes/
│ │ │ ├── ClientesApplication.java # Punto de entrada
│ │ │ ├── controller/
│ │ │ │ └── ClientesController.java # Endpoints REST
│ │ │ ├── model/
│ │ │ │ └── ClientesModel.java # Entidad con validaciones
│ │ │ ├── service/
│ │ │ │ └── ClientesService.java # Lógica de negocio
│ │ │ ├── repository/
│ │ │ │ └── ClientesRepository.java # Acceso a datos
│ │ │ └── exception/
│ │ │ └── GlobalExceptionHandler.java # Manejo de errores
│ │ └── resources/
│ │ └── application.properties # Configuración
│ └── test/
│ └── ClientesApplicationTests.java # Pruebas
└── pom.xml # Configuración Maven

🎯 Conceptos Clave

1. Decoradores (Anotaciones) Básicos de Spring

Las anotaciones son etiquetas especiales que proporcionan metadatos sobre el programa, no affecting directly the operation of the code but providing information to the framework.

@SpringBootApplication

Ubicación: ClientesApplication.java

@SpringBootApplicationpublicclassClientesApplication {
publicstaticvoidmain(String[] args) {
SpringApplication.run(ClientesApplication.class, args);
}
}

¿Qué hace? Combina tres anotaciones en una:

  • @Configuration: Marca la clase como fuente de definiciones de beans
  • @EnableAutoConfiguration: Permite que Spring Boot configure automáticamente la aplicación basándose en las dependencias
  • @ComponentScan: Escanea el paquete actual y subpaquetes buscando componentes anotados

@RestController

Ubicación: ClientesController.java

@RestController@RequestMapping("/api/v1/clientes")
publicclassClientesController {
// ...
}

¿Qué hace?

  • Marca la clase como controlador REST
  • Equivalente a @Controller + @ResponseBody
  • Los métodos retornan datos serializados (JSON) automáticamente
  • @RequestMapping("/api/v1/clientes"): Define la ruta base para todos los endpoints

@Autowired

Ubicación: ClientesController.java

@AutowiredprivateClientesServiceclientesService;

¿Qué hace?

  • Inyección de Dependencias: Spring inyecta automáticamente una instancia de ClientesService
  • No necesitas crear la instancia manualmente con new
  • Spring gestiona el ciclo de vida del objeto
  • Promueve el desacoplamiento y facilita las pruebas

@Service

Ubicación: ClientesService.java

@ServicepublicclassClientesService {
// ...
}

¿Qué hace?

  • Marca la clase como un bean de servicio (componente del negocio)
  • Contiene la lógica empresarial
  • Spring la detecta automáticamente y la registra como bean
  • Se puede inyectar en otros componentes

@Repository

Ubicación: ClientesRepository.java

@RepositorypublicclassClientesRepository {
// ...
}

¿Qué hace?

  • Marca la clase como un repositorio (capa de acceso a datos)
  • Responsable de CRUD (Create, Read, Update, Delete)
  • En este proyecto, usa un ArrayList en memoria (simulación)
  • En proyectos reales, se conectaría a una base de datos

2. Decoradores de Validación (Validación de Datos)

Garantizan que los datos recibidos cumplan con reglas específicas antes de procesarlos.

Ubicación: ClientesModel.java

@Data@NoArgsConstructor@AllArgsConstructorpublicclassClientesModel {
@NotNull(message = "El nombre no puede ser null")
@NotBlank(message = "El nombre no puede estar vacio")
privateStringnombre;
@NotNull(message = "El correo no puede ser null")
@NotBlank(message = "El correo no puede estar vacio")
privateStringcorreo;
@NotNull(message = "La edad no puede ser null")
@Positive(message = "La edad debe ser mayor a cero")
privateIntegeredad;
}

@NotNull

  • Valida: El valor no puede ser null
  • Tipo: Cualquier tipo de datos
  • Diferencia con @NotBlank: Permite strings vacíos ""

@NotBlank

  • Valida: El string no puede ser null ni estar vacío
  • Trim: Considera espacios en blanco como vacío
  • Tipo: Solo strings

@Positive

  • Valida: El número debe ser mayor que 0
  • Tipo: Números (Integer, Double, BigDecimal, etc.)

Decoradores de Lombok

@Data// Genera: getters, setters, equals(), hashCode(), toString()@NoArgsConstructor// Genera constructor sin argumentos@AllArgsConstructor// Genera constructor con todos los campos

3. Mapeo de Endpoints REST

Verbos HTTP y Decoradores Correspondientes

@RestController@RequestMapping("/api/v1/clientes")
publicclassClientesController {
// GET - Obtener todos los clientes@GetMappingpublicResponseEntity<List<ClientesModel>> listarClientes() { }
// POST - Crear nuevo cliente@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@Valid@RequestBodyClientesModelcliente
) { }
// DELETE - Eliminar cliente por correo@DeleteMapping("{correo}")
publicResponseEntity<String> eliminarCliente(
@PathVariableStringcorreo
) { }
}
DecoratorVerbo HTTPOperaciónDescripción
@GetMappingGETReadObtiene datos sin modificarlos
@PostMappingPOSTCreateCrea un nuevo recurso
@PutMappingPUTUpdateActualiza completamente un recurso
@PatchMappingPATCHPartial UpdateActualiza parcialmente un recurso
@DeleteMappingDELETEDeleteElimina un recurso

Parámetros Importantes

// @RequestBody: Mapea el JSON del request al objeto@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@RequestBodyClientesModelcliente
) { }
// @Valid: Activa la validación de datos@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@Valid@RequestBodyClientesModelcliente
) { }
// @PathVariable: Extrae valores de la ruta@DeleteMapping("{correo}")
publicResponseEntity<String> eliminarCliente(
@PathVariableStringcorreo
) { }

4. HTTP Status Codes (Códigos de Estado)

Los códigos HTTP comunican al cliente el resultado de su request.

Códigos Utilizados en Este Proyecto

CódigoNombreSignificadoCuándo Usar
200OKLa solicitud fue exitosaGET exitoso, DELETE exitoso
201CreatedRecurso creado exitosamentePOST que crea un recurso
400Bad RequestSolicitud inválida o error en validaciónDatos inválidos, errores de negocio
404Not FoundRecurso no encontradoGET de recurso inexistente
500Internal Server ErrorError del servidorExcepciones no controladas

Ejemplo de Uso en el Proyecto

// GET exitoso (200)@GetMappingpublicResponseEntity<List<ClientesModel>> listarClientes() {
returnResponseEntity.status(200).body(clientesService.getClientes());
}
// POST exitoso (201)@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(@Valid@RequestBodyClientesModelcliente) {
returnResponseEntity.status(201).body(clientesService.saveCliente(cliente));
}
// Error en validación (400)// Manejado automáticamente por GlobalExceptionHandler

5. Manejo Centralizado de Excepciones

En lugar de manejar errores en cada endpoint, Spring permite centralizar la gestión en una clase especial.

Ubicación: GlobalExceptionHandler.java

@RestControllerAdvicepublicclassGlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
publicResponseEntity<Map<String, String>> handleValidationErrors(
MethodArgumentNotValidExceptionex
) {
Map<String, String> errores = newHashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errores.put(error.getField(), error.getDefaultMessage())
);
returnResponseEntity.status(HttpStatus.BAD_REQUEST).body(errores);
}
}

¿Cómo funciona?

  1. @RestControllerAdvice: Marca la clase como handler global de excepciones
  2. @ExceptionHandler: Define qué tipo de excepción maneja este método
  3. Cuando alguien envía un cliente sin nombre, Spring:
    • Lanza MethodArgumentNotValidException
    • GlobalExceptionHandler lo captura
    • Retorna un JSON con los errores específicos

Respuesta de Error (ejemplo):

{
"nombre": "El nombre no puede estar vacio",
"edad": "La edad debe ser mayor a cero"
}

🚀 Crear un Nuevo Proyecto con Spring Initializr

Si deseas crear un proyecto similar desde cero, puedes usar Spring Initializr:

Opción 1: Web (Recomendado para principiantes)

  1. Accede a: start.spring.io
  2. Configura:
    • Project: Maven Project
    • Language: Java
    • Spring Boot: 4.0.4 o superior
    • Group: com.duoc
    • Artifact: clientes
    • Java Version: 21
  3. Dependencias (Add Dependencies):
    • Spring Web
    • Spring Boot Validation
    • Lombok
  4. Click "Generate"
  5. Descomprime y abre en tu IDE favorito

Opción 2: Línea de Comandos

curl https://start.spring.io/starter.zip \
-d dependencies=web,validation,lombok \
-d javaVersion=21 \
-d bootVersion=4.0.4 \
-d groupId=com.duoc \
-d artifactId=clientes \
-o clientes.zip
unzip clientes.zip
cd clientes

✅ Paso a Paso para Ejecutar el Proyecto

Paso 1: Clonar o Descargar el Proyecto

cd tu-directorio
# Si es un repositorio git:
git clone <repositorio>cd clientes

Paso 2: Verificar Java

java -version
# Debe mostrar Java 21 o superior

Paso 3: Compilar el Proyecto

mvn clean compile
  • clean: Elimina compilaciones anteriores
  • compile: Compila el código fuente

Paso 4: Ejecutar la Aplicación

mvn spring-boot:run

Salida esperada:

[INFO] Started ClientesApplication in 2.5 seconds

La aplicación estará disponible en: http://localhost:8080


🧪 Pruebas de Endpoints

Herramientas Recomendadas

  • Postman: Interfaz gráfica completa
  • cURL: Línea de comandos (incluido en macOS/Linux)
  • Thunder Client: Extensión de VS Code
  • Insomnia: Similar a Postman

Ejemplos con cURL

1. Listar todos los clientes (GET)

curl -X GET http://localhost:8080/api/v1/clientes

Respuesta esperada (200):

[]

2. Crear un cliente válido (POST)

curl -X POST http://localhost:8080/api/v1/clientes \
-H "Content-Type: application/json" \
-d '{ "nombre": "Juan Pérez", "correo": "juan@example.com", "edad": 25 }'

Respuesta esperada (201):

{
"nombre": "Juan Pérez",
"correo": "juan@example.com",
"edad": 25
}

3. Intentar crear cliente con datos inválidos (POST)

curl -X POST http://localhost:8080/api/v1/clientes \
-H "Content-Type: application/json" \
-d '{ "nombre": "", "correo": "juan@example.com", "edad": -5 }'

Respuesta esperada (400):

{
"nombre": "El nombre no puede estar vacio",
"edad": "La edad debe ser mayor a cero"
}

4. Listar clientes después de crear uno (GET)

curl -X GET http://localhost:8080/api/v1/clientes

Respuesta esperada (200):

[
{
"nombre": "Juan Pérez",
"correo": "juan@example.com",
"edad": 25
}
]

5. Eliminar un cliente (DELETE)

curl -X DELETE http://localhost:8080/api/v1/clientes/juan@example.com

Respuesta esperada (200):

"Cliente eliminado"

📊 Flujo de Datos en la Aplicación

┌─────────────────────────────────────────────────────────────────┐
│ CLIENTE (Postman, cURL) │
└────────────────────────┬──────────────────────────────────────────┘
│ 1. Envía JSON
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesController │
│ - Recibe request HTTP │
│ - Mapea JSON → ClientesModel (@RequestBody) │
│ - Valida datos (@Valid) │
│ - Si hay errores → GlobalExceptionHandler (captura) │
└────────────────────────┬──────────────────────────────────────────┘
│ 2. Si válido, llama servicio
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesService │
│ - Contiene lógica de negocio │
│ - Procesa los datos │
│ - Llama al repositorio │
└────────────────────────┬──────────────────────────────────────────┘
│ 3. Persistencia
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesRepository │
│ - ArrayList (simula base de datos en memoria) │
│ - CRUD: Crear, Leer, Actualizar, Eliminar │
│ - Retorna datos procesados │
└────────────────────────┬──────────────────────────────────────────┘
│ 4. Respuesta
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesController │
│ - ResponseEntity with HTTP Status (200, 201, 400) │
│ - Serializa objeto → JSON │
└────────────────────────┬──────────────────────────────────────────┘
│ 5. Envía JSON
▼
┌─────────────────────────────────────────────────────────────────┐
│ CLIENTE (Recibe respuesta) │
└─────────────────────────────────────────────────────────────────┘

🔄 Patrón de Diseño CSR (Controller-Service-Repository)

CSR es la versión adaptada del patrón MVC específicamente para Spring Boot y APIs REST.

CapaComponenteArchivoResponsabilidad
PresentaciónControllerClientesController.javaMapea rutas HTTP, recibe requests, retorna responses
Lógica de NegocioServiceClientesService.javaContiene la lógica de negocio, orquesta operaciones
DatosRepositoryClientesRepository.javaAcceso a datos, persistencia, operaciones CRUD
DatosModelClientesModel.javaRepresenta la entidad con atributos y validaciones

¿Por qué CSR en lugar de MVC?

En aplicaciones REST con Spring Boot:

  • No hay "View" tradicional (HTML) → La respuesta es JSON
  • La separación de responsabilidades es más clara
  • Service maneja la lógica de negocio (no el Controller)
  • Repository aísla el acceso a datos
  • Facilita testing y mantenimiento

Ventajas del Patrón CSR

✅ Código más limpio y organizado
✅ Fácil de testear (inyección de dependencias)
✅ Escalable (añadir funcionalidades sin afectar otras capas)
✅ Reutilizable (Service puede usarse desde múltiples Controllers)
✅ Mantenible (cambios aislados por capa)


💡 Buenas Prácticas Implementadas

1. Separación de Responsabilidades

Cada clase tiene una único propósito claro:

  • Controller: Mapeo HTTP
  • Service: Lógica de negocio
  • Repository: Acceso a datos

2. Inyección de Dependencias

@AutowiredprivateClientesServiceclientesService;

Facilita: Testing, desacoplamiento, mantenibilidad

3. Validación de Datos a la Entrada

@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(@Valid@RequestBodyClientesModelcliente)

Previene procesamiento de datos inválidos

4. Códigos de Estado HTTP Apropiados

  • 200: Operación exitosa
  • 201: Recurso creado
  • 400: Solicitud inválida
  • 500: Error del servidor

5. Manejo Centralizado de Excepciones

En lugar de try-catch en cada método, usar @RestControllerAdvice

6. Versionamiento de API

/api/v1/clientes permite evitar conflictos al cambiar la API


📖 Ejercicios Propuestos

Nivel 1: Básico

  1. Agregar un nuevo campo telefono a ClientesModel con validación
  2. Crear un endpoint PUT para actualizar cliente
  3. Agregar validación de email usando @Email

Nivel 2: Intermedio

  1. Agregar manejo de excepción personalizada (ej: ClienteNoEncontradoException)
  2. Implementar búsqueda de cliente por correo (GET con parámetro)
  3. Agregar información de error más detallada en respuestas

📝 Recursos Adicionales


🐛 Solución de Problemas Comunes

Error: "Port 8080 already in use"

# Cambiar puerto en application.properties:
server.port=8081

Error: "Field clientesService required a bean of type"

Este error significa que la inyección de dependencias no encontró el bean. Asegurate:

  • La clase ClientesService tiene @Service
  • El controlador tiene @Autowired
  • Spring escanea el paquete correcto

Error: "JSON parse error"

El JSON enviado tiene formato inválido. Verifica:

  • Comillas dobles en propiedades
  • Tipos de datos correctos
  • No hay caracteres de escape faltantes

📄 Licencia

Este proyecto es de código abierto y está disponible bajo la licencia MIT, diseñado para propósitos educativos.


Última actualización: 28 de marzo de 2026

About

Proyecto educativo de Fullstack I para estudiantes de segundo año de Ingeniería en Informática: API REST en Spring Boot para gestión de clientes, aplicando validaciones, arquitectura por capas y buenas prácticas HTTP.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - raicerk/fullstack-i-clientes: Proyecto educativo de Fullstack I para estudiantes de segundo año de Ingeniería en Informática: API REST en Spring Boot para gestión de clientes, aplicando validaciones, arquitectura por capas y buenas prácticas HTTP. · GitHub
Skip to content

Repository files navigation

Proyecto: Gestión de Clientes con Spring Boot

Un proyecto educativo diseñado para estudiantes de segundo año de Ingeniería en Computación que enseña los principios fundamentales de Spring Framework, decoradores básicos y de validación, buenas prácticas de APIs REST con HTTP status codes, y manejo centralizado de errores.

Este ejercicio corresponde al ramo Fullstack I para estudiantes de Ingeniería en Informática de DUOC UC.

📚 Objetivos de Aprendizaje

  • Comprender la arquitectura de una aplicación Spring Boot
  • Dominar decoradores (anotaciones) básicas y de validación
  • Implementar APIs REST siguiendo buenas prácticas
  • Usar códigos de estado HTTP apropiados
  • Implementar manejo centralizado de excepciones
  • Aplicar inyección de dependencias
  • Utilizar el patrón de diseño CSR (Controller-Service-Repository): adaptación de MVC para Spring Boot

🔧 Requisitos Previos

  • Java 21 o superior
  • Maven 3.6+ para gestión de dependencias
  • IDE recomendado: IntelliJ IDEA, Visual Studio Code o Eclipse
  • Conocimientos básicos de Java y POO
  • Conceptos básicos de APIs REST y HTTP

📦 Dependencias Principales

<!-- Spring Boot Starter Web (REST Controllers y servidores web) -->
spring-boot-starter-web
<!-- Spring Boot Starter Validation (Validación de datos) -->
spring-boot-starter-validation
<!-- Lombok (Generación automática de getters, setters, etc.) -->
lombok
<!-- Spring Boot Starter Test (Pruebas unitarias) -->
spring-boot-starter-test

🏗️ Estructura del Proyecto

clientes/
├── src/
│ ├── main/
│ │ ├── java/com/duoc/clientes/
│ │ │ ├── ClientesApplication.java # Punto de entrada
│ │ │ ├── controller/
│ │ │ │ └── ClientesController.java # Endpoints REST
│ │ │ ├── model/
│ │ │ │ └── ClientesModel.java # Entidad con validaciones
│ │ │ ├── service/
│ │ │ │ └── ClientesService.java # Lógica de negocio
│ │ │ ├── repository/
│ │ │ │ └── ClientesRepository.java # Acceso a datos
│ │ │ └── exception/
│ │ │ └── GlobalExceptionHandler.java # Manejo de errores
│ │ └── resources/
│ │ └── application.properties # Configuración
│ └── test/
│ └── ClientesApplicationTests.java # Pruebas
└── pom.xml # Configuración Maven

🎯 Conceptos Clave

1. Decoradores (Anotaciones) Básicos de Spring

Las anotaciones son etiquetas especiales que proporcionan metadatos sobre el programa, no affecting directly the operation of the code but providing information to the framework.

@SpringBootApplication

Ubicación: ClientesApplication.java

@SpringBootApplicationpublicclassClientesApplication {
publicstaticvoidmain(String[] args) {
SpringApplication.run(ClientesApplication.class, args);
}
}

¿Qué hace? Combina tres anotaciones en una:

  • @Configuration: Marca la clase como fuente de definiciones de beans
  • @EnableAutoConfiguration: Permite que Spring Boot configure automáticamente la aplicación basándose en las dependencias
  • @ComponentScan: Escanea el paquete actual y subpaquetes buscando componentes anotados

@RestController

Ubicación: ClientesController.java

@RestController@RequestMapping("/api/v1/clientes")
publicclassClientesController {
// ...
}

¿Qué hace?

  • Marca la clase como controlador REST
  • Equivalente a @Controller + @ResponseBody
  • Los métodos retornan datos serializados (JSON) automáticamente
  • @RequestMapping("/api/v1/clientes"): Define la ruta base para todos los endpoints

@Autowired

Ubicación: ClientesController.java

@AutowiredprivateClientesServiceclientesService;

¿Qué hace?

  • Inyección de Dependencias: Spring inyecta automáticamente una instancia de ClientesService
  • No necesitas crear la instancia manualmente con new
  • Spring gestiona el ciclo de vida del objeto
  • Promueve el desacoplamiento y facilita las pruebas

@Service

Ubicación: ClientesService.java

@ServicepublicclassClientesService {
// ...
}

¿Qué hace?

  • Marca la clase como un bean de servicio (componente del negocio)
  • Contiene la lógica empresarial
  • Spring la detecta automáticamente y la registra como bean
  • Se puede inyectar en otros componentes

@Repository

Ubicación: ClientesRepository.java

@RepositorypublicclassClientesRepository {
// ...
}

¿Qué hace?

  • Marca la clase como un repositorio (capa de acceso a datos)
  • Responsable de CRUD (Create, Read, Update, Delete)
  • En este proyecto, usa un ArrayList en memoria (simulación)
  • En proyectos reales, se conectaría a una base de datos

2. Decoradores de Validación (Validación de Datos)

Garantizan que los datos recibidos cumplan con reglas específicas antes de procesarlos.

Ubicación: ClientesModel.java

@Data@NoArgsConstructor@AllArgsConstructorpublicclassClientesModel {
@NotNull(message = "El nombre no puede ser null")
@NotBlank(message = "El nombre no puede estar vacio")
privateStringnombre;
@NotNull(message = "El correo no puede ser null")
@NotBlank(message = "El correo no puede estar vacio")
privateStringcorreo;
@NotNull(message = "La edad no puede ser null")
@Positive(message = "La edad debe ser mayor a cero")
privateIntegeredad;
}

@NotNull

  • Valida: El valor no puede ser null
  • Tipo: Cualquier tipo de datos
  • Diferencia con @NotBlank: Permite strings vacíos ""

@NotBlank

  • Valida: El string no puede ser null ni estar vacío
  • Trim: Considera espacios en blanco como vacío
  • Tipo: Solo strings

@Positive

  • Valida: El número debe ser mayor que 0
  • Tipo: Números (Integer, Double, BigDecimal, etc.)

Decoradores de Lombok

@Data// Genera: getters, setters, equals(), hashCode(), toString()@NoArgsConstructor// Genera constructor sin argumentos@AllArgsConstructor// Genera constructor con todos los campos

3. Mapeo de Endpoints REST

Verbos HTTP y Decoradores Correspondientes

@RestController@RequestMapping("/api/v1/clientes")
publicclassClientesController {
// GET - Obtener todos los clientes@GetMappingpublicResponseEntity<List<ClientesModel>> listarClientes() { }
// POST - Crear nuevo cliente@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@Valid@RequestBodyClientesModelcliente
) { }
// DELETE - Eliminar cliente por correo@DeleteMapping("{correo}")
publicResponseEntity<String> eliminarCliente(
@PathVariableStringcorreo
) { }
}
DecoratorVerbo HTTPOperaciónDescripción
@GetMappingGETReadObtiene datos sin modificarlos
@PostMappingPOSTCreateCrea un nuevo recurso
@PutMappingPUTUpdateActualiza completamente un recurso
@PatchMappingPATCHPartial UpdateActualiza parcialmente un recurso
@DeleteMappingDELETEDeleteElimina un recurso

Parámetros Importantes

// @RequestBody: Mapea el JSON del request al objeto@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@RequestBodyClientesModelcliente
) { }
// @Valid: Activa la validación de datos@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@Valid@RequestBodyClientesModelcliente
) { }
// @PathVariable: Extrae valores de la ruta@DeleteMapping("{correo}")
publicResponseEntity<String> eliminarCliente(
@PathVariableStringcorreo
) { }

4. HTTP Status Codes (Códigos de Estado)

Los códigos HTTP comunican al cliente el resultado de su request.

Códigos Utilizados en Este Proyecto

CódigoNombreSignificadoCuándo Usar
200OKLa solicitud fue exitosaGET exitoso, DELETE exitoso
201CreatedRecurso creado exitosamentePOST que crea un recurso
400Bad RequestSolicitud inválida o error en validaciónDatos inválidos, errores de negocio
404Not FoundRecurso no encontradoGET de recurso inexistente
500Internal Server ErrorError del servidorExcepciones no controladas

Ejemplo de Uso en el Proyecto

// GET exitoso (200)@GetMappingpublicResponseEntity<List<ClientesModel>> listarClientes() {
returnResponseEntity.status(200).body(clientesService.getClientes());
}
// POST exitoso (201)@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(@Valid@RequestBodyClientesModelcliente) {
returnResponseEntity.status(201).body(clientesService.saveCliente(cliente));
}
// Error en validación (400)// Manejado automáticamente por GlobalExceptionHandler

5. Manejo Centralizado de Excepciones

En lugar de manejar errores en cada endpoint, Spring permite centralizar la gestión en una clase especial.

Ubicación: GlobalExceptionHandler.java

@RestControllerAdvicepublicclassGlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
publicResponseEntity<Map<String, String>> handleValidationErrors(
MethodArgumentNotValidExceptionex
) {
Map<String, String> errores = newHashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errores.put(error.getField(), error.getDefaultMessage())
);
returnResponseEntity.status(HttpStatus.BAD_REQUEST).body(errores);
}
}

¿Cómo funciona?

  1. @RestControllerAdvice: Marca la clase como handler global de excepciones
  2. @ExceptionHandler: Define qué tipo de excepción maneja este método
  3. Cuando alguien envía un cliente sin nombre, Spring:
    • Lanza MethodArgumentNotValidException
    • GlobalExceptionHandler lo captura
    • Retorna un JSON con los errores específicos

Respuesta de Error (ejemplo):

{
"nombre": "El nombre no puede estar vacio",
"edad": "La edad debe ser mayor a cero"
}

🚀 Crear un Nuevo Proyecto con Spring Initializr

Si deseas crear un proyecto similar desde cero, puedes usar Spring Initializr:

Opción 1: Web (Recomendado para principiantes)

  1. Accede a: start.spring.io
  2. Configura:
    • Project: Maven Project
    • Language: Java
    • Spring Boot: 4.0.4 o superior
    • Group: com.duoc
    • Artifact: clientes
    • Java Version: 21
  3. Dependencias (Add Dependencies):
    • Spring Web
    • Spring Boot Validation
    • Lombok
  4. Click "Generate"
  5. Descomprime y abre en tu IDE favorito

Opción 2: Línea de Comandos

curl https://start.spring.io/starter.zip \
-d dependencies=web,validation,lombok \
-d javaVersion=21 \
-d bootVersion=4.0.4 \
-d groupId=com.duoc \
-d artifactId=clientes \
-o clientes.zip
unzip clientes.zip
cd clientes

✅ Paso a Paso para Ejecutar el Proyecto

Paso 1: Clonar o Descargar el Proyecto

cd tu-directorio
# Si es un repositorio git:
git clone <repositorio>cd clientes

Paso 2: Verificar Java

java -version
# Debe mostrar Java 21 o superior

Paso 3: Compilar el Proyecto

mvn clean compile
  • clean: Elimina compilaciones anteriores
  • compile: Compila el código fuente

Paso 4: Ejecutar la Aplicación

mvn spring-boot:run

Salida esperada:

[INFO] Started ClientesApplication in 2.5 seconds

La aplicación estará disponible en: http://localhost:8080


🧪 Pruebas de Endpoints

Herramientas Recomendadas

  • Postman: Interfaz gráfica completa
  • cURL: Línea de comandos (incluido en macOS/Linux)
  • Thunder Client: Extensión de VS Code
  • Insomnia: Similar a Postman

Ejemplos con cURL

1. Listar todos los clientes (GET)

curl -X GET http://localhost:8080/api/v1/clientes

Respuesta esperada (200):

[]

2. Crear un cliente válido (POST)

curl -X POST http://localhost:8080/api/v1/clientes \
-H "Content-Type: application/json" \
-d '{ "nombre": "Juan Pérez", "correo": "juan@example.com", "edad": 25 }'

Respuesta esperada (201):

{
"nombre": "Juan Pérez",
"correo": "juan@example.com",
"edad": 25
}

3. Intentar crear cliente con datos inválidos (POST)

curl -X POST http://localhost:8080/api/v1/clientes \
-H "Content-Type: application/json" \
-d '{ "nombre": "", "correo": "juan@example.com", "edad": -5 }'

Respuesta esperada (400):

{
"nombre": "El nombre no puede estar vacio",
"edad": "La edad debe ser mayor a cero"
}

4. Listar clientes después de crear uno (GET)

curl -X GET http://localhost:8080/api/v1/clientes

Respuesta esperada (200):

[
{
"nombre": "Juan Pérez",
"correo": "juan@example.com",
"edad": 25
}
]

5. Eliminar un cliente (DELETE)

curl -X DELETE http://localhost:8080/api/v1/clientes/juan@example.com

Respuesta esperada (200):

"Cliente eliminado"

📊 Flujo de Datos en la Aplicación

┌─────────────────────────────────────────────────────────────────┐
│ CLIENTE (Postman, cURL) │
└────────────────────────┬──────────────────────────────────────────┘
│ 1. Envía JSON
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesController │
│ - Recibe request HTTP │
│ - Mapea JSON → ClientesModel (@RequestBody) │
│ - Valida datos (@Valid) │
│ - Si hay errores → GlobalExceptionHandler (captura) │
└────────────────────────┬──────────────────────────────────────────┘
│ 2. Si válido, llama servicio
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesService │
│ - Contiene lógica de negocio │
│ - Procesa los datos │
│ - Llama al repositorio │
└────────────────────────┬──────────────────────────────────────────┘
│ 3. Persistencia
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesRepository │
│ - ArrayList (simula base de datos en memoria) │
│ - CRUD: Crear, Leer, Actualizar, Eliminar │
│ - Retorna datos procesados │
└────────────────────────┬──────────────────────────────────────────┘
│ 4. Respuesta
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesController │
│ - ResponseEntity with HTTP Status (200, 201, 400) │
│ - Serializa objeto → JSON │
└────────────────────────┬──────────────────────────────────────────┘
│ 5. Envía JSON
▼
┌─────────────────────────────────────────────────────────────────┐
│ CLIENTE (Recibe respuesta) │
└─────────────────────────────────────────────────────────────────┘

🔄 Patrón de Diseño CSR (Controller-Service-Repository)

CSR es la versión adaptada del patrón MVC específicamente para Spring Boot y APIs REST.

CapaComponenteArchivoResponsabilidad
PresentaciónControllerClientesController.javaMapea rutas HTTP, recibe requests, retorna responses
Lógica de NegocioServiceClientesService.javaContiene la lógica de negocio, orquesta operaciones
DatosRepositoryClientesRepository.javaAcceso a datos, persistencia, operaciones CRUD
DatosModelClientesModel.javaRepresenta la entidad con atributos y validaciones

¿Por qué CSR en lugar de MVC?

En aplicaciones REST con Spring Boot:

  • No hay "View" tradicional (HTML) → La respuesta es JSON
  • La separación de responsabilidades es más clara
  • Service maneja la lógica de negocio (no el Controller)
  • Repository aísla el acceso a datos
  • Facilita testing y mantenimiento

Ventajas del Patrón CSR

✅ Código más limpio y organizado
✅ Fácil de testear (inyección de dependencias)
✅ Escalable (añadir funcionalidades sin afectar otras capas)
✅ Reutilizable (Service puede usarse desde múltiples Controllers)
✅ Mantenible (cambios aislados por capa)


💡 Buenas Prácticas Implementadas

1. Separación de Responsabilidades

Cada clase tiene una único propósito claro:

  • Controller: Mapeo HTTP
  • Service: Lógica de negocio
  • Repository: Acceso a datos

2. Inyección de Dependencias

@AutowiredprivateClientesServiceclientesService;

Facilita: Testing, desacoplamiento, mantenibilidad

3. Validación de Datos a la Entrada

@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(@Valid@RequestBodyClientesModelcliente)

Previene procesamiento de datos inválidos

4. Códigos de Estado HTTP Apropiados

  • 200: Operación exitosa
  • 201: Recurso creado
  • 400: Solicitud inválida
  • 500: Error del servidor

5. Manejo Centralizado de Excepciones

En lugar de try-catch en cada método, usar @RestControllerAdvice

6. Versionamiento de API

/api/v1/clientes permite evitar conflictos al cambiar la API


📖 Ejercicios Propuestos

Nivel 1: Básico

  1. Agregar un nuevo campo telefono a ClientesModel con validación
  2. Crear un endpoint PUT para actualizar cliente
  3. Agregar validación de email usando @Email

Nivel 2: Intermedio

  1. Agregar manejo de excepción personalizada (ej: ClienteNoEncontradoException)
  2. Implementar búsqueda de cliente por correo (GET con parámetro)
  3. Agregar información de error más detallada en respuestas

📝 Recursos Adicionales


🐛 Solución de Problemas Comunes

Error: "Port 8080 already in use"

# Cambiar puerto en application.properties:
server.port=8081

Error: "Field clientesService required a bean of type"

Este error significa que la inyección de dependencias no encontró el bean. Asegurate:

  • La clase ClientesService tiene @Service
  • El controlador tiene @Autowired
  • Spring escanea el paquete correcto

Error: "JSON parse error"

El JSON enviado tiene formato inválido. Verifica:

  • Comillas dobles en propiedades
  • Tipos de datos correctos
  • No hay caracteres de escape faltantes

📄 Licencia

Este proyecto es de código abierto y está disponible bajo la licencia MIT, diseñado para propósitos educativos.


Última actualización: 28 de marzo de 2026

About

Proyecto educativo de Fullstack I para estudiantes de segundo año de Ingeniería en Informática: API REST en Spring Boot para gestión de clientes, aplicando validaciones, arquitectura por capas y buenas prácticas HTTP.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - raicerk/fullstack-i-clientes: Proyecto educativo de Fullstack I para estudiantes de segundo año de Ingeniería en Informática: API REST en Spring Boot para gestión de clientes, aplicando validaciones, arquitectura por capas y buenas prácticas HTTP. · GitHub
Skip to content

Repository files navigation

Proyecto: Gestión de Clientes con Spring Boot

Un proyecto educativo diseñado para estudiantes de segundo año de Ingeniería en Computación que enseña los principios fundamentales de Spring Framework, decoradores básicos y de validación, buenas prácticas de APIs REST con HTTP status codes, y manejo centralizado de errores.

Este ejercicio corresponde al ramo Fullstack I para estudiantes de Ingeniería en Informática de DUOC UC.

📚 Objetivos de Aprendizaje

  • Comprender la arquitectura de una aplicación Spring Boot
  • Dominar decoradores (anotaciones) básicas y de validación
  • Implementar APIs REST siguiendo buenas prácticas
  • Usar códigos de estado HTTP apropiados
  • Implementar manejo centralizado de excepciones
  • Aplicar inyección de dependencias
  • Utilizar el patrón de diseño CSR (Controller-Service-Repository): adaptación de MVC para Spring Boot

🔧 Requisitos Previos

  • Java 21 o superior
  • Maven 3.6+ para gestión de dependencias
  • IDE recomendado: IntelliJ IDEA, Visual Studio Code o Eclipse
  • Conocimientos básicos de Java y POO
  • Conceptos básicos de APIs REST y HTTP

📦 Dependencias Principales

<!-- Spring Boot Starter Web (REST Controllers y servidores web) -->
spring-boot-starter-web
<!-- Spring Boot Starter Validation (Validación de datos) -->
spring-boot-starter-validation
<!-- Lombok (Generación automática de getters, setters, etc.) -->
lombok
<!-- Spring Boot Starter Test (Pruebas unitarias) -->
spring-boot-starter-test

🏗️ Estructura del Proyecto

clientes/
├── src/
│ ├── main/
│ │ ├── java/com/duoc/clientes/
│ │ │ ├── ClientesApplication.java # Punto de entrada
│ │ │ ├── controller/
│ │ │ │ └── ClientesController.java # Endpoints REST
│ │ │ ├── model/
│ │ │ │ └── ClientesModel.java # Entidad con validaciones
│ │ │ ├── service/
│ │ │ │ └── ClientesService.java # Lógica de negocio
│ │ │ ├── repository/
│ │ │ │ └── ClientesRepository.java # Acceso a datos
│ │ │ └── exception/
│ │ │ └── GlobalExceptionHandler.java # Manejo de errores
│ │ └── resources/
│ │ └── application.properties # Configuración
│ └── test/
│ └── ClientesApplicationTests.java # Pruebas
└── pom.xml # Configuración Maven

🎯 Conceptos Clave

1. Decoradores (Anotaciones) Básicos de Spring

Las anotaciones son etiquetas especiales que proporcionan metadatos sobre el programa, no affecting directly the operation of the code but providing information to the framework.

@SpringBootApplication

Ubicación: ClientesApplication.java

@SpringBootApplicationpublicclassClientesApplication {
publicstaticvoidmain(String[] args) {
SpringApplication.run(ClientesApplication.class, args);
}
}

¿Qué hace? Combina tres anotaciones en una:

  • @Configuration: Marca la clase como fuente de definiciones de beans
  • @EnableAutoConfiguration: Permite que Spring Boot configure automáticamente la aplicación basándose en las dependencias
  • @ComponentScan: Escanea el paquete actual y subpaquetes buscando componentes anotados

@RestController

Ubicación: ClientesController.java

@RestController@RequestMapping("/api/v1/clientes")
publicclassClientesController {
// ...
}

¿Qué hace?

  • Marca la clase como controlador REST
  • Equivalente a @Controller + @ResponseBody
  • Los métodos retornan datos serializados (JSON) automáticamente
  • @RequestMapping("/api/v1/clientes"): Define la ruta base para todos los endpoints

@Autowired

Ubicación: ClientesController.java

@AutowiredprivateClientesServiceclientesService;

¿Qué hace?

  • Inyección de Dependencias: Spring inyecta automáticamente una instancia de ClientesService
  • No necesitas crear la instancia manualmente con new
  • Spring gestiona el ciclo de vida del objeto
  • Promueve el desacoplamiento y facilita las pruebas

@Service

Ubicación: ClientesService.java

@ServicepublicclassClientesService {
// ...
}

¿Qué hace?

  • Marca la clase como un bean de servicio (componente del negocio)
  • Contiene la lógica empresarial
  • Spring la detecta automáticamente y la registra como bean
  • Se puede inyectar en otros componentes

@Repository

Ubicación: ClientesRepository.java

@RepositorypublicclassClientesRepository {
// ...
}

¿Qué hace?

  • Marca la clase como un repositorio (capa de acceso a datos)
  • Responsable de CRUD (Create, Read, Update, Delete)
  • En este proyecto, usa un ArrayList en memoria (simulación)
  • En proyectos reales, se conectaría a una base de datos

2. Decoradores de Validación (Validación de Datos)

Garantizan que los datos recibidos cumplan con reglas específicas antes de procesarlos.

Ubicación: ClientesModel.java

@Data@NoArgsConstructor@AllArgsConstructorpublicclassClientesModel {
@NotNull(message = "El nombre no puede ser null")
@NotBlank(message = "El nombre no puede estar vacio")
privateStringnombre;
@NotNull(message = "El correo no puede ser null")
@NotBlank(message = "El correo no puede estar vacio")
privateStringcorreo;
@NotNull(message = "La edad no puede ser null")
@Positive(message = "La edad debe ser mayor a cero")
privateIntegeredad;
}

@NotNull

  • Valida: El valor no puede ser null
  • Tipo: Cualquier tipo de datos
  • Diferencia con @NotBlank: Permite strings vacíos ""

@NotBlank

  • Valida: El string no puede ser null ni estar vacío
  • Trim: Considera espacios en blanco como vacío
  • Tipo: Solo strings

@Positive

  • Valida: El número debe ser mayor que 0
  • Tipo: Números (Integer, Double, BigDecimal, etc.)

Decoradores de Lombok

@Data// Genera: getters, setters, equals(), hashCode(), toString()@NoArgsConstructor// Genera constructor sin argumentos@AllArgsConstructor// Genera constructor con todos los campos

3. Mapeo de Endpoints REST

Verbos HTTP y Decoradores Correspondientes

@RestController@RequestMapping("/api/v1/clientes")
publicclassClientesController {
// GET - Obtener todos los clientes@GetMappingpublicResponseEntity<List<ClientesModel>> listarClientes() { }
// POST - Crear nuevo cliente@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@Valid@RequestBodyClientesModelcliente
) { }
// DELETE - Eliminar cliente por correo@DeleteMapping("{correo}")
publicResponseEntity<String> eliminarCliente(
@PathVariableStringcorreo
) { }
}
DecoratorVerbo HTTPOperaciónDescripción
@GetMappingGETReadObtiene datos sin modificarlos
@PostMappingPOSTCreateCrea un nuevo recurso
@PutMappingPUTUpdateActualiza completamente un recurso
@PatchMappingPATCHPartial UpdateActualiza parcialmente un recurso
@DeleteMappingDELETEDeleteElimina un recurso

Parámetros Importantes

// @RequestBody: Mapea el JSON del request al objeto@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@RequestBodyClientesModelcliente
) { }
// @Valid: Activa la validación de datos@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(
@Valid@RequestBodyClientesModelcliente
) { }
// @PathVariable: Extrae valores de la ruta@DeleteMapping("{correo}")
publicResponseEntity<String> eliminarCliente(
@PathVariableStringcorreo
) { }

4. HTTP Status Codes (Códigos de Estado)

Los códigos HTTP comunican al cliente el resultado de su request.

Códigos Utilizados en Este Proyecto

CódigoNombreSignificadoCuándo Usar
200OKLa solicitud fue exitosaGET exitoso, DELETE exitoso
201CreatedRecurso creado exitosamentePOST que crea un recurso
400Bad RequestSolicitud inválida o error en validaciónDatos inválidos, errores de negocio
404Not FoundRecurso no encontradoGET de recurso inexistente
500Internal Server ErrorError del servidorExcepciones no controladas

Ejemplo de Uso en el Proyecto

// GET exitoso (200)@GetMappingpublicResponseEntity<List<ClientesModel>> listarClientes() {
returnResponseEntity.status(200).body(clientesService.getClientes());
}
// POST exitoso (201)@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(@Valid@RequestBodyClientesModelcliente) {
returnResponseEntity.status(201).body(clientesService.saveCliente(cliente));
}
// Error en validación (400)// Manejado automáticamente por GlobalExceptionHandler

5. Manejo Centralizado de Excepciones

En lugar de manejar errores en cada endpoint, Spring permite centralizar la gestión en una clase especial.

Ubicación: GlobalExceptionHandler.java

@RestControllerAdvicepublicclassGlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
publicResponseEntity<Map<String, String>> handleValidationErrors(
MethodArgumentNotValidExceptionex
) {
Map<String, String> errores = newHashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errores.put(error.getField(), error.getDefaultMessage())
);
returnResponseEntity.status(HttpStatus.BAD_REQUEST).body(errores);
}
}

¿Cómo funciona?

  1. @RestControllerAdvice: Marca la clase como handler global de excepciones
  2. @ExceptionHandler: Define qué tipo de excepción maneja este método
  3. Cuando alguien envía un cliente sin nombre, Spring:
    • Lanza MethodArgumentNotValidException
    • GlobalExceptionHandler lo captura
    • Retorna un JSON con los errores específicos

Respuesta de Error (ejemplo):

{
"nombre": "El nombre no puede estar vacio",
"edad": "La edad debe ser mayor a cero"
}

🚀 Crear un Nuevo Proyecto con Spring Initializr

Si deseas crear un proyecto similar desde cero, puedes usar Spring Initializr:

Opción 1: Web (Recomendado para principiantes)

  1. Accede a: start.spring.io
  2. Configura:
    • Project: Maven Project
    • Language: Java
    • Spring Boot: 4.0.4 o superior
    • Group: com.duoc
    • Artifact: clientes
    • Java Version: 21
  3. Dependencias (Add Dependencies):
    • Spring Web
    • Spring Boot Validation
    • Lombok
  4. Click "Generate"
  5. Descomprime y abre en tu IDE favorito

Opción 2: Línea de Comandos

curl https://start.spring.io/starter.zip \
-d dependencies=web,validation,lombok \
-d javaVersion=21 \
-d bootVersion=4.0.4 \
-d groupId=com.duoc \
-d artifactId=clientes \
-o clientes.zip
unzip clientes.zip
cd clientes

✅ Paso a Paso para Ejecutar el Proyecto

Paso 1: Clonar o Descargar el Proyecto

cd tu-directorio
# Si es un repositorio git:
git clone <repositorio>cd clientes

Paso 2: Verificar Java

java -version
# Debe mostrar Java 21 o superior

Paso 3: Compilar el Proyecto

mvn clean compile
  • clean: Elimina compilaciones anteriores
  • compile: Compila el código fuente

Paso 4: Ejecutar la Aplicación

mvn spring-boot:run

Salida esperada:

[INFO] Started ClientesApplication in 2.5 seconds

La aplicación estará disponible en: http://localhost:8080


🧪 Pruebas de Endpoints

Herramientas Recomendadas

  • Postman: Interfaz gráfica completa
  • cURL: Línea de comandos (incluido en macOS/Linux)
  • Thunder Client: Extensión de VS Code
  • Insomnia: Similar a Postman

Ejemplos con cURL

1. Listar todos los clientes (GET)

curl -X GET http://localhost:8080/api/v1/clientes

Respuesta esperada (200):

[]

2. Crear un cliente válido (POST)

curl -X POST http://localhost:8080/api/v1/clientes \
-H "Content-Type: application/json" \
-d '{ "nombre": "Juan Pérez", "correo": "juan@example.com", "edad": 25 }'

Respuesta esperada (201):

{
"nombre": "Juan Pérez",
"correo": "juan@example.com",
"edad": 25
}

3. Intentar crear cliente con datos inválidos (POST)

curl -X POST http://localhost:8080/api/v1/clientes \
-H "Content-Type: application/json" \
-d '{ "nombre": "", "correo": "juan@example.com", "edad": -5 }'

Respuesta esperada (400):

{
"nombre": "El nombre no puede estar vacio",
"edad": "La edad debe ser mayor a cero"
}

4. Listar clientes después de crear uno (GET)

curl -X GET http://localhost:8080/api/v1/clientes

Respuesta esperada (200):

[
{
"nombre": "Juan Pérez",
"correo": "juan@example.com",
"edad": 25
}
]

5. Eliminar un cliente (DELETE)

curl -X DELETE http://localhost:8080/api/v1/clientes/juan@example.com

Respuesta esperada (200):

"Cliente eliminado"

📊 Flujo de Datos en la Aplicación

┌─────────────────────────────────────────────────────────────────┐
│ CLIENTE (Postman, cURL) │
└────────────────────────┬──────────────────────────────────────────┘
│ 1. Envía JSON
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesController │
│ - Recibe request HTTP │
│ - Mapea JSON → ClientesModel (@RequestBody) │
│ - Valida datos (@Valid) │
│ - Si hay errores → GlobalExceptionHandler (captura) │
└────────────────────────┬──────────────────────────────────────────┘
│ 2. Si válido, llama servicio
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesService │
│ - Contiene lógica de negocio │
│ - Procesa los datos │
│ - Llama al repositorio │
└────────────────────────┬──────────────────────────────────────────┘
│ 3. Persistencia
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesRepository │
│ - ArrayList (simula base de datos en memoria) │
│ - CRUD: Crear, Leer, Actualizar, Eliminar │
│ - Retorna datos procesados │
└────────────────────────┬──────────────────────────────────────────┘
│ 4. Respuesta
▼
┌─────────────────────────────────────────────────────────────────┐
│ ClientesController │
│ - ResponseEntity with HTTP Status (200, 201, 400) │
│ - Serializa objeto → JSON │
└────────────────────────┬──────────────────────────────────────────┘
│ 5. Envía JSON
▼
┌─────────────────────────────────────────────────────────────────┐
│ CLIENTE (Recibe respuesta) │
└─────────────────────────────────────────────────────────────────┘

🔄 Patrón de Diseño CSR (Controller-Service-Repository)

CSR es la versión adaptada del patrón MVC específicamente para Spring Boot y APIs REST.

CapaComponenteArchivoResponsabilidad
PresentaciónControllerClientesController.javaMapea rutas HTTP, recibe requests, retorna responses
Lógica de NegocioServiceClientesService.javaContiene la lógica de negocio, orquesta operaciones
DatosRepositoryClientesRepository.javaAcceso a datos, persistencia, operaciones CRUD
DatosModelClientesModel.javaRepresenta la entidad con atributos y validaciones

¿Por qué CSR en lugar de MVC?

En aplicaciones REST con Spring Boot:

  • No hay "View" tradicional (HTML) → La respuesta es JSON
  • La separación de responsabilidades es más clara
  • Service maneja la lógica de negocio (no el Controller)
  • Repository aísla el acceso a datos
  • Facilita testing y mantenimiento

Ventajas del Patrón CSR

✅ Código más limpio y organizado
✅ Fácil de testear (inyección de dependencias)
✅ Escalable (añadir funcionalidades sin afectar otras capas)
✅ Reutilizable (Service puede usarse desde múltiples Controllers)
✅ Mantenible (cambios aislados por capa)


💡 Buenas Prácticas Implementadas

1. Separación de Responsabilidades

Cada clase tiene una único propósito claro:

  • Controller: Mapeo HTTP
  • Service: Lógica de negocio
  • Repository: Acceso a datos

2. Inyección de Dependencias

@AutowiredprivateClientesServiceclientesService;

Facilita: Testing, desacoplamiento, mantenibilidad

3. Validación de Datos a la Entrada

@PostMappingpublicResponseEntity<ClientesModel> agregarCliente(@Valid@RequestBodyClientesModelcliente)

Previene procesamiento de datos inválidos

4. Códigos de Estado HTTP Apropiados

  • 200: Operación exitosa
  • 201: Recurso creado
  • 400: Solicitud inválida
  • 500: Error del servidor

5. Manejo Centralizado de Excepciones

En lugar de try-catch en cada método, usar @RestControllerAdvice

6. Versionamiento de API

/api/v1/clientes permite evitar conflictos al cambiar la API


📖 Ejercicios Propuestos

Nivel 1: Básico

  1. Agregar un nuevo campo telefono a ClientesModel con validación
  2. Crear un endpoint PUT para actualizar cliente
  3. Agregar validación de email usando @Email

Nivel 2: Intermedio

  1. Agregar manejo de excepción personalizada (ej: ClienteNoEncontradoException)
  2. Implementar búsqueda de cliente por correo (GET con parámetro)
  3. Agregar información de error más detallada en respuestas

📝 Recursos Adicionales


🐛 Solución de Problemas Comunes

Error: "Port 8080 already in use"

# Cambiar puerto en application.properties:
server.port=8081

Error: "Field clientesService required a bean of type"

Este error significa que la inyección de dependencias no encontró el bean. Asegurate:

  • La clase ClientesService tiene @Service
  • El controlador tiene @Autowired
  • Spring escanea el paquete correcto

Error: "JSON parse error"

El JSON enviado tiene formato inválido. Verifica:

  • Comillas dobles en propiedades
  • Tipos de datos correctos
  • No hay caracteres de escape faltantes

📄 Licencia

Este proyecto es de código abierto y está disponible bajo la licencia MIT, diseñado para propósitos educativos.


Última actualización: 28 de marzo de 2026

About

Proyecto educativo de Fullstack I para estudiantes de segundo año de Ingeniería en Informática: API REST en Spring Boot para gestión de clientes, aplicando validaciones, arquitectura por capas y buenas prácticas HTTP.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages