Quiz: Capa de Servicio y Arquitectura
Ponga a prueba sus conocimientos sobre la capa de servicio, la validación y la arquitectura MVC.
30 preguntas - Haga clic en cada pregunta para revelar la respuesta.
Preguntas 1-10: Capa de Servicio
Pregunta 1: ¿Cuál es el rol de la capa de Servicio en la arquitectura MVC?
Respuesta: La capa de Servicio contiene la lógica de negocio de la aplicación. Esta:
- Orquesta las operaciones entre controladores y repositorios
- Aplica las reglas de negocio
- Gestiona las transacciones
- Es independiente del protocolo HTTP
Pregunta 2: ¿Qué anotación se utiliza para declarar un servicio en Spring?
Respuesta: @Service - Es un estereotipo especializado de @Component que indica que la clase contiene lógica de negocio.
@Service
public class UserService {
// lógica de negocio
}
Pregunta 3: ¿Por qué no poner la lógica de negocio en el controlador?
Respuesta: Separar la lógica de negocio del controlador permite:
- Reutilización: el servicio puede ser llamado por varios controladores
- Testabilidad: probar la lógica sin HTTP
- Mantenibilidad: código más organizado
- Separación de responsabilidades: cada capa tiene un rol específico
Pregunta 4: ¿Cómo inyectar un Repository en un Service?
Respuesta: Por inyección de constructor (recomendado):
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
Pregunta 5: ¿Qué es un DTO (Data Transfer Object)?
Respuesta: Un DTO es un objeto simple utilizado para transferir datos entre capas. Este:
- No contiene lógica de negocio
- Expone solo los datos necesarios
- Protege la entidad JPA de la exposición directa
- Permite formatear los datos para la API
Pregunta 6: ¿Por qué usar DTOs en lugar de las entidades JPA directamente?
Respuesta:
- Seguridad: no exponer campos sensibles (password)
- Rendimiento: evitar la carga lazy de relaciones
- Flexibilidad: formato diferente al de la entidad
- Desacoplamiento: cambios internos no afectan la API
- Validación: reglas específicas para entrada/salida
Pregunta 7: ¿Cómo convertir una entidad en un DTO?
Respuesta: Varias aproximaciones:
// 1. Método manual
public UserDto toDto(User user) {
return new UserDto(user.getId(), user.getName(), user.getEmail());
}
// 2. Con MapStruct
@Mapper
public interface UserMapper {
UserDto toDto(User user);
}
// 3. Con ModelMapper
modelMapper.map(user, UserDto.class);
Pregunta 8: ¿Cuál es la diferencia entre un RequestDTO y un ResponseDTO?
Respuesta:
- RequestDTO: datos entrantes (creación, actualización), contiene la validación
- ResponseDTO: datos salientes, formateados para el cliente
// Request
public class CreateUserRequest {
@NotBlank
private String email;
@Size(min = 8)
private String password;
}
// Response
public class UserResponse {
private Long id;
private String email;
// ¡Sin password!
}
Pregunta 9: ¿Cómo manejar los errores en la capa de Servicio?
Respuesta: Lanzar excepciones personalizadas:
@Service
public class UserService {
public User findById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException("User not found: " + id));
}
}
Estas excepciones luego son gestionadas por @ControllerAdvice.
Pregunta 10: ¿Qué es @Transactional en un servicio?
Respuesta: @Transactional gestiona automáticamente las transacciones:
- Inicia una transacción al principio del método
- Commit si tiene éxito
- Rollback si hay excepción
@Service
@Transactional
public class OrderService {
// Todos los métodos son transaccionales
}
Preguntas 11-20: Validación
Pregunta 11: ¿Qué dependencia agregar para la validación?
Respuesta: spring-boot-starter-validation
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Pregunta 12: ¿Cómo activar la validación en un controlador?
Respuesta: Con @Valid o @Validated en el parámetro:
@PostMapping
public ResponseEntity<User> create(@Valid @RequestBody CreateUserRequest request) {
// Si la validación falla, se lanza MethodArgumentNotValidException
}
Pregunta 13: ¿Cuáles son las anotaciones de validación más comunes?
Respuesta:
| Anotación | Descripción |
|---|---|
@NotNull | No debe ser null |
@NotBlank | No null, no vacío, sin espacios |
@NotEmpty | No null y no vacío |
@Size(min, max) | Tamaño entre min y max |
@Min, @Max | Valor numérico mínimo/máximo |
@Email | Formato de email válido |
@Pattern | Regex personalizada |
@Past, @Future | Fecha en pasado/futuro |
Pregunta 14: ¿Cómo personalizar el mensaje de error de validación?
Respuesta: Con el atributo message:
public class CreateUserRequest {
@NotBlank(message = "El email es obligatorio")
@Email(message = "Formato de email inválido")
private String email;
@Size(min = 8, message = "La contraseña debe tener al menos 8 caracteres")
private String password;
}
Pregunta 15: ¿Cómo crear una validación personalizada?
Respuesta: Crear una anotación y un validador:
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueEmailValidator.class)
public @interface UniqueEmail {
String message() default "Email ya utilizado";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {
@Override
public boolean isValid(String email, ConstraintValidatorContext context) {
// Verificar si el email existe
}
}
Pregunta 16: ¿Cuál es la diferencia entre @Valid y @Validated?
Respuesta:
@Valid: estándar Java (JSR-380), cascada la validación@Validated: Spring, soporta grupos de validación
// Grupos de validación
public interface OnCreate {}
public interface OnUpdate {}
@NotNull(groups = OnUpdate.class)
private Long id;
@Validated(OnCreate.class)
public void create(...) { }
Pregunta 17: ¿Cómo validar los parámetros de consulta (query params)?
Respuesta: Agregar @Validated en el controlador:
@RestController
@Validated
public class UserController {
@GetMapping("/users")
public List<User> search(
@RequestParam @Min(0) int page,
@RequestParam @Max(100) int size) {
// ...
}
}
Pregunta 18: ¿Cómo validar un objeto anidado?
Respuesta: Usar @Valid en el campo anidado:
public class OrderRequest {
@Valid // Cascada la validación
@NotNull
private AddressDto shippingAddress;
}
public class AddressDto {
@NotBlank
private String street;
@NotBlank
private String city;
}
Pregunta 19: ¿Cómo recuperar los errores de validación?
Respuesta: Vía BindingResult o @ExceptionHandler:
// Opción 1: BindingResult
@PostMapping
public ResponseEntity<?> create(@Valid @RequestBody Request req, BindingResult result) {
if (result.hasErrors()) {
return ResponseEntity.badRequest().body(result.getAllErrors());
}
}
// Opción 2: ControllerAdvice (recomendado)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidation(MethodArgumentNotValidException ex) {
// Extraer los errores
}
Pregunta 20: ¿Qué es la validación a nivel de servicio?
Respuesta: Validación de la lógica de negocio (no solo el formato):
@Service
public class OrderService {
public Order createOrder(OrderRequest request) {
// Validación de negocio
if (request.getQuantity() > productService.getStock(request.getProductId())) {
throw new InsufficientStockException("Stock insuficiente");
}
// ...
}
}
Preguntas 21-30: Arquitectura
Pregunta 21: ¿Cuáles son las 3 capas principales de la arquitectura MVC?
Respuesta:
- Controller (Presentación): gestiona HTTP, validación de entrada
- Service (Negocio): lógica de negocio, transacciones
- Repository (Datos): acceso a base de datos
Cliente → Controller → Service → Repository → Database
Pregunta 22: ¿En qué capa colocar la gestión de transacciones?
Respuesta: En la capa Service. Es ahí donde se encuentra la lógica de negocio que define lo que constituye una unidad de trabajo atómica.
@Service
@Transactional
public class TransferService {
public void transfer(Long fromId, Long toId, BigDecimal amount) {
// Toda la operación es atómica
}
}
Pregunta 23: ¿Cómo organizar los paquetes de un proyecto Spring Boot?
Respuesta: Por capa o por funcionalidad:
// Por capa
com.example.app
├── controller/
├── service/
├── repository/
├── model/
└── dto/
// Por funcionalidad (recomendado para proyectos grandes)
com.example.app
├── user/
│ ├── UserController
│ ├── UserService
│ └── UserRepository
├── order/
└── product/
Pregunta 24: ¿Qué es el principio de responsabilidad única (SRP)?
Respuesta: Una clase debe tener una sola razón para cambiar. Cada clase tiene una única responsabilidad:
- Controller: gestionar las solicitudes HTTP
- Service: lógica de negocio
- Repository: acceso a datos
Mal ejemplo: un controlador que hace todo (HTTP + negocio + BD).
Pregunta 25: ¿Por qué usar interfaces para los servicios?
Respuesta:
- Desacoplamiento: los clientes dependen de la abstracción
- Testabilidad: fácil de mockear
- Flexibilidad: cambiar la implementación
- Proxying: Spring AOP funciona mejor
public interface UserService {
User findById(Long id);
}
@Service
public class UserServiceImpl implements UserService { }
Pregunta 26: ¿Qué es un @ControllerAdvice?
Respuesta: Un componente que gestiona las excepciones globalmente para todos los controladores:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(UserNotFoundException ex) {
return ResponseEntity.status(404).body(new ErrorResponse(ex.getMessage()));
}
}
Pregunta 27: ¿Cómo estructurar una respuesta de error estándar?
Respuesta: Crear un DTO de error coherente:
public class ErrorResponse {
private LocalDateTime timestamp;
private int status;
private String error;
private String message;
private String path;
}
// O formato ProblemDetail (RFC 7807)
Pregunta 28: ¿Cuál es la diferencia entre @RestController y @Controller?
Respuesta:
@Controller: retorna vistas (HTML)@RestController: retorna datos (JSON/XML)
@RestController = @Controller + @ResponseBody
@RestController // Todo es JSON
public class UserController { }
@Controller
public class ViewController {
@GetMapping("/")
public String home() {
return "index"; // Vista Thymeleaf
}
}
Pregunta 29: ¿Cómo manejar los códigos HTTP apropiados?
Respuesta: Usar ResponseEntity:
@PostMapping
public ResponseEntity<User> create(@RequestBody UserRequest req) {
User user = userService.create(req);
return ResponseEntity
.status(HttpStatus.CREATED) // 201
.body(user);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build(); // 204
}
Pregunta 30: ¿Cuáles son los códigos HTTP comunes en REST?
Respuesta:
| Código | Significado | Uso |
|---|---|---|
| 200 | OK | GET, PUT exitoso |
| 201 | Created | POST exitoso |
| 204 | No Content | DELETE exitoso |
| 400 | Bad Request | Validación fallida |
| 401 | Unauthorized | No autenticado |
| 403 | Forbidden | No autorizado |
| 404 | Not Found | Recurso inexistente |
| 500 | Server Error | Error interno |
Puntuación
| Puntuación | Nivel |
|---|---|
| 27-30 | Excelente - Arquitectura dominada |
| 21-26 | Bien - Algunas revisiones |
| 15-20 | Medio - Repase los capítulos |
| Menos de 15 | A revisar - Repase los fundamentos |