1. Crear el proyecto con Spring Initializr1. Create the Project with Spring Initializr
La forma más rápida de arrancar un proyecto Spring Boot es con start.spring.io. Selecciona estas dependencias base: The fastest way to start a Spring Boot project is with start.spring.io. Select these base dependencies:
- Spring Web — servidor HTTP embebido (Tomcat)Spring Web — embedded HTTP server (Tomcat)
- Spring Data JPA — ORM con HibernateSpring Data JPA — ORM with Hibernate
- PostgreSQL Driver — conector JDBCPostgreSQL Driver — JDBC connector
- Spring Security — seguridad y autenticaciónSpring Security — security and authentication
- Lombok — reduce el boilerplate (getters, constructores)Lombok — reduces boilerplate (getters, constructors)
# application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=postgres
spring.datasource.password=${DB_PASSWORD}
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=false 2. Entidad JPA y repositorio2. JPA Entity and Repository
Las entidades JPA mapean directamente a tablas de la base de datos. Spring Data JPA genera el SQL automáticamente a partir de los métodos del repositorio: JPA entities map directly to database tables. Spring Data JPA automatically generates SQL from repository method names:
@Entity
@Table(name = "users")
@Data // Lombok
@NoArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String email;
@Column(nullable = false)
private String name;
@Column(name = "password_hash", nullable = false)
private String passwordHash;
@CreationTimestamp
private LocalDateTime createdAt;
}
// Repositorio — Spring genera las queries automáticamente
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
boolean existsByEmail(String email);
} 3. Capa de servicio3. Service Layer
La capa de servicio contiene la lógica de negocio. Nunca pongas lógica de negocio en los controladores ni en los repositorios: The service layer contains the business logic. Never put business logic in controllers or repositories:
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepo;
private final PasswordEncoder encoder;
public UserResponse createUser(CreateUserRequest req) {
if (userRepo.existsByEmail(req.email())) {
throw new ConflictException("Email already registered");
}
var user = new User();
user.setEmail(req.email().toLowerCase());
user.setName(req.name());
user.setPasswordHash(encoder.encode(req.password()));
return UserResponse.from(userRepo.save(user));
}
public UserResponse findById(Long id) {
return userRepo.findById(id)
.map(UserResponse::from)
.orElseThrow(() -> new NotFoundException("User not found"));
}
} 4. Controlador REST4. REST Controller
@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public UserResponse create(@RequestBody @Valid CreateUserRequest req) {
return userService.createUser(req);
}
@GetMapping("/{id}")
public UserResponse getById(@PathVariable Long id) {
return userService.findById(id);
}
@GetMapping("/me")
public UserResponse getCurrentUser(Authentication auth) {
return userService.findByEmail(auth.getName());
}
} 5. Patrón DTO con Java Records5. DTO Pattern with Java Records
Nunca expongas las entidades JPA directamente en las respuestas de la API. Los DTOs (Data Transfer Objects) desacoplan la API del modelo de datos y mejoran la seguridad: Never expose JPA entities directly in API responses. DTOs (Data Transfer Objects) decouple the API from the data model and improve security:
// Request DTO con validación
public record CreateUserRequest(
@NotBlank @Email String email,
@NotBlank @Size(min = 2, max = 100) String name,
@NotBlank @Size(min = 8) String password
) {}
// Response DTO — nunca expone passwordHash
public record UserResponse(Long id, String email, String name, LocalDateTime createdAt) {
public static UserResponse from(User user) {
return new UserResponse(user.getId(), user.getEmail(),
user.getName(), user.getCreatedAt());
}
} 6. JWT con Spring Security6. JWT with Spring Security
Spring Security 6 usa un modelo basado en componentes (no XML). Configura el filtro JWT como un OncePerRequestFilter y registra el SecurityFilterChain como bean: Spring Security 6 uses a component-based model (no XML). Configure the JWT filter as a OncePerRequestFilter and register the SecurityFilterChain as a bean:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(s -> s.sessionCreationPolicy(STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
.build();
} ConclusiónConclusion
Spring Boot sigue siendo la opción más sólida para backends enterprise en Java. La combinación de JPA, Spring Security y DTOs con Java Records te da una arquitectura robusta, segura y perfectamente alineada con el ecosistema Java moderno. Spring Boot remains the most solid choice for enterprise Java backends. The combination of JPA, Spring Security and DTOs with Java Records gives you a robust, secure architecture perfectly aligned with the modern Java ecosystem.
¿Necesitas un backend Spring Boot para tu empresa? Contáctame. Desarrollo APIs enterprise con Java Spring Boot + PostgreSQL para clientes en Colombia y Latinoamérica. Need a Spring Boot backend for your company? Contact me. I develop enterprise APIs with Java Spring Boot + PostgreSQL for clients in Colombia and Latin America.