Arquitectura recomendada de un proyecto
Cuando las aplicaciones crecen, el código puede volverse difícil de mantener.
Por eso es importante organizar bien el proyecto desde el principio.
En este capítulo aprenderemos:
- la arquitectura por capas
- cómo organizar paquetes
- qué es un Controller
- qué es un Service
- qué es un Repository
- qué son los DTO
Esta arquitectura es la más usada en proyectos Spring Boot profesionales.
1. Arquitectura por capas #
La arquitectura más común en Spring Boot es la arquitectura en capas.
El flujo típico de una aplicación es:
Cliente
↓
Controller
↓
Service
↓
Repository
↓
Base de datos
Cada capa tiene una responsabilidad diferente.
2. Estructura recomendada de paquetes #
Una estructura típica es esta:
com.example.app
controller
service
repository
entity
dto
config
Ejemplo completo:
src/main/java/com/example/app
controller
UserController.java
service
UserService.java
repository
UserRepository.java
entity
User.java
dto
UserDTO.java
Esta organización hace el código mucho más claro.
3. Controller #
El Controller recibe las peticiones HTTP.
Por ejemplo:
GET /users
POST /users
DELETE /users
El controller no debe contener lógica compleja.
Su función es:
- recibir peticiones
- llamar al service
- devolver respuesta
4. Ejemplo #
1@RestController 2@RequestMapping("/users") 3public class UserController { 4 5 private final UserService service; 6 7 public UserController(UserService service) { 8 this.service = service; 9 } 10 11 @GetMapping 12 public List<User> getUsers() { 13 return service.getAllUsers(); 14 } 15 16}
El controller simplemente llama al service.
5. Service #
La capa Service contiene la lógica de negocio.
Ejemplos de lógica de negocio:
- validar datos
- aplicar reglas
- procesar información
- coordinar repositorios
6. Ejemplo #
1@Service 2public class UserService { 3 4 private final UserRepository repository; 5 6 public UserService(UserRepository repository) { 7 this.repository = repository; 8 } 9 10 public List<User> getAllUsers() { 11 return repository.findAll(); 12 } 13 14}
El service utiliza el repository para acceder a la base de datos.
7. Repository #
El Repository se encarga de acceder a la base de datos.
En Spring Boot normalmente usamos Spring Data JPA.
Ejemplo:
1@Repository 2public interface UserRepository extends JpaRepository<User, Long> { 3 4}
Esto automáticamente genera:
findAll()
findById()
save()
delete()
Sin escribir código.
8. Entity #
Una Entity representa una tabla de la base de datos.
Ejemplo:
Tabla:
users
Entidad:
1@Entity 2public class User { 3 4 @Id 5 @GeneratedValue 6 private Long id; 7 8 private String name; 9 10 private String email; 11 12}
Cada objeto corresponde a un registro de la tabla.
9. DTO (Data Transfer Object) #
Un DTO es un objeto que se usa para transferir datos entre capas.
Se utiliza para evitar enviar entidades directamente.
10. Por qué no devolver entidades #
Si devolvemos entidades:
- podemos exponer datos sensibles
- podemos generar problemas de rendimiento
- podemos acoplar la API a la base de datos
Por eso se usan DTOs.
11. Ejemplo DTO #
1public class UserDTO { 2 3 private Long id; 4 private String name; 5 6}
Este DTO no incluye el email.
12. Flujo completo de una petición #
Supongamos esta petición:
GET /users
El flujo sería:
Cliente
↓
UserController
↓
UserService
↓
UserRepository
↓
Base de datos
Después vuelve la respuesta.
13. Ejemplo completo #
Entity #
1@Entity 2public class User { 3 4 @Id 5 @GeneratedValue 6 private Long id; 7 8 private String name; 9 10}
Repository #
1@Repository 2public interface UserRepository extends JpaRepository<User, Long> { 3 4}
Service #
1@Service 2public class UserService { 3 4 private final UserRepository repository; 5 6 public UserService(UserRepository repository) { 7 this.repository = repository; 8 } 9 10 public List<User> getUsers() { 11 return repository.findAll(); 12 } 13 14}
Controller #
1@RestController 2@RequestMapping("/users") 3public class UserController { 4 5 private final UserService service; 6 7 public UserController(UserService service) { 8 this.service = service; 9 } 10 11 @GetMapping 12 public List<User> getUsers() { 13 return service.getUsers(); 14 } 15 16}
14. Ventajas de esta arquitectura #
Esta arquitectura tiene muchas ventajas.
Código más organizado #
Cada clase tiene una función clara.
Más fácil de mantener #
Podemos modificar una capa sin afectar a otras.
Más fácil de probar #
Podemos probar services sin controllers.
Más fácil de escalar #
Las aplicaciones grandes siguen esta estructura.
15. Buenas prácticas #
Algunas recomendaciones importantes.
Controller ligero #
El controller debe ser simple.
❌ Incorrecto:
controller con lógica compleja
✔ Correcto:
controller llama al service
Service con lógica #
La lógica de negocio debe estar en el service.
Repository solo para datos #
El repository solo accede a la base de datos.
Usar DTOs #
Evita devolver entidades directamente.
Resumen del tema
Conceptos clave #
- Separación de Responsabilidades por Capas:
- Controller (
@RestController): enrutamiento HTTP, deserialización de peticiones, validación básica y respuesta. - Service (
@Service): reglas de negocio, transaccionalidad (@Transactional) y orquestación de repositorios. - Repository (
@Repository/JpaRepository): operaciones de persistencia CRUD y consultas en base de datos.
- Controller (
- Entidades (
@Entity) vs DTOs (Data Transfer Objects): desacoplamiento del modelo relacional frente al contrato de la API para proteger datos sensibles y optimizar payloads. - Organización de Paquetes: estructuración limpia por capas funcionales (
controller,service,repository,entity,dto,config).
Qué debes recordar #
Mantén los controladores delgados (thin controllers), delega toda la lógica de negocio en los servicios y desacopla la base de datos de la API pública utilizando DTOs.