Estructura profesional de proyectos
Cuando los proyectos crecen mucho (miles de líneas de código), una mala organización del proyecto provoca:
- código difícil de mantener
- dependencias cruzadas
- errores difíciles de encontrar
- dificultad para añadir nuevas funcionalidades
Por eso las empresas utilizan estructuras de proyecto profesionales.
1. Estructura básica (proyectos pequeños) #
En proyectos simples suele usarse esta estructura:
1com.example.app 2 3controller 4service 5repository 6entity 7dto 8config
Ejemplo:
1controller 2 UserController 3 4service 5 UserService 6 7repository 8 UserRepository
Esta estructura funciona bien para proyectos pequeños.
2. Problema cuando el proyecto crece #
Imagina que el sistema tiene:
- usuarios
- cursos
- pagos
- notificaciones
- reportes
La estructura quedaría así:
1controller 2 UserController 3 CourseController 4 PaymentController 5 ReportController 6 7service 8 UserService 9 CourseService 10 PaymentService 11 ReportService
Esto provoca:
- carpetas enormes
- difícil localizar código
- mezcla de dominios
3. Estructura por módulos (recomendada) #
Las empresas suelen organizar por dominio o funcionalidad.
Ejemplo:
1com.example.app 2 3user 4 controller 5 service 6 repository 7 entity 8 dto 9 10course 11 controller 12 service 13 repository 14 entity 15 dto 16 17payment 18 controller 19 service 20 repository 21 entity
Esto mejora:
- organización
- mantenibilidad
- escalabilidad
4. Ejemplo real #
1com.example.courseapp 2 3config 4security 5exception 6 7user 8 controller 9 service 10 repository 11 entity 12 dto 13 14course 15 controller 16 service 17 repository 18 entity 19 dto 20 21enrollment 22 controller 23 service 24 repository 25 entity
Cada módulo contiene todo lo necesario para esa funcionalidad.
5. Separar capa de infraestructura #
En proyectos grandes también se separa:
1domain 2application 3infrastructure
Ejemplo:
1domain 2 model 3 repository 4 5application 6 service 7 usecase 8 9infrastructure 10 controller 11 persistence 12 config
Esto se usa en:
- Clean Architecture
- Arquitectura Hexagonal
6. Ejemplo completo profesional #
1com.example.app 2 3config 4security 5exception 6 7user 8 controller 9 service 10 repository 11 entity 12 dto 13 mapper 14 15course 16 controller 17 service 18 repository 19 entity 20 dto 21 mapper 22 23enrollment 24 controller 25 service 26 repository 27 entity
7. Usar Mapper para convertir DTO #
En proyectos grandes se usan mappers.
Ejemplo con MapStruct.
8. DTO -> Entity #
1@Mapper(componentModel = "spring") 2public interface UserMapper { 3 4 User toEntity(UserDTO dto); 5 6 UserDTO toDTO(User user); 7 8}
Esto evita escribir conversiones manuales.
9. Separar lógica de negocio #
Un error común es poner lógica en los controllers.
Incorrecto:
1@PostMapping 2public User create(@RequestBody UserDTO dto) { 3 4 User user = new User(); 5 6 user.setName(dto.getName()); 7 8 return repository.save(user); 9 10}
Correcto:
1@PostMapping 2public User create(@RequestBody UserDTO dto) { 3 4 return service.createUser(dto); 5 6}
La lógica debe estar en services.
10. Manejo global de errores #
En proyectos profesionales se usa un GlobalExceptionHandler.
11. Ejemplo #
1@RestControllerAdvice 2public class GlobalExceptionHandler { 3 4 @ExceptionHandler(RuntimeException.class) 5 public ResponseEntity<String> handle(RuntimeException ex) { 6 7 return ResponseEntity 8 .status(HttpStatus.BAD_REQUEST) 9 .body(ex.getMessage()); 10 11 } 12 13}
Esto centraliza los errores.
12. Paginación en APIs #
Nunca devolver listas enormes.
13. Ejemplo #
1@GetMapping 2public Page<User> getUsers(Pageable pageable) { 3 4 return repository.findAll(pageable); 5 6}
Petición:
1/users?page=0&size=10
14. Logging profesional #
Usar SLF4J.
1private static final Logger log = 2 LoggerFactory.getLogger(UserService.class);
Ejemplo:
1log.info("Creando usuario {}", email);
15. Configuración por entornos #
Separar configuración:
1application-dev.properties 2application-prod.properties
Activar perfil:
1spring.profiles.active=dev
16. Buenas prácticas profesionales #
usar DTO #
Nunca devolver entidades directamente.
separar capas #
Controller → Service → Repository.
validación de datos #
Usar anotaciones:
1@NotNull 2@Email 3@Size
manejar errores globalmente #
Usar @RestControllerAdvice.
17. Ejemplo de arquitectura limpia #
1controller 2 recibe petición HTTP 3 4service 5 lógica de negocio 6 7repository 8 acceso a base de datos 9 10entity 11 modelo de datos 12 13dto 14 objetos de transferencia
18. Estructura final recomendada #
1com.example.app 2 3config 4security 5exception 6 7user 8course 9payment 10notification
Cada módulo contiene:
1controller 2service 3repository 4entity 5dto 6mapper
Esto es lo más usado en proyectos profesionales.
Resumen del tema
Conceptos clave #
- Package by Feature / Módulos: organización del código por dominio funcional (
user,course,payment) en lugar de empaquetar por capas técnicas aisladas, facilitando la escalabilidad y mantenibilidad. - Separación estricta de responsabilidades: controladores delegando exclusivamente en servicios de aplicación, evitando lógica de negocio o accesos directos a repositorios en la capa web.
- Mapeo automatizado (MapStruct): transformación desacoplada entre DTOs y entidades de dominio mediante interfaces
@Mappergeneradas en tiempo de compilación. - Estandarización transversal: centralización del manejo de excepciones (
@RestControllerAdvice), logging estructurado (SLF4J/Logback) y perfiles de configuración desacoplados (application-dev.properties,application-prod.properties). - Paginación y rendimiento: exposición de endpoints paginados (
Pageable,Page<T>) para evitar transferencias masivas de datos y sobrecarga de memoria.
Qué debes recordar #
Estructura los proyectos empresariales por paquetes de dominio (Package by Feature) o capas arquitectónicas limpias, aislando lógica de negocio, mapeando con DTOs y centralizando errores y configuración.