CodeGym /Cursos /Módulo 5. Spring /Resumen de las lecciones anteriores

Resumen de las lecciones anteriores

Módulo 5. Spring
Nivel 25 , Lección 1
Disponible

Antes de ponernos a escribir código, es importante definir el estilo arquitectónico de la aplicación, sus componentes principales y cómo interactúan entre sí. Cualquier programador experimentado confirmará que una arquitectura mal diseñada es como una casa sin cimientos: puede aguantar, pero no por mucho.


1. Visión arquitectónica

Elección del estilo arquitectónico

Desarrollamos la aplicación con una arquitectura monolítica. Sí, sí, nada de hype con microservicios en esta fase. ¿Por qué? Porque nuestra aplicación será relativamente pequeña, y pasarse a microservicios ahora no tiene sentido. Todo estará en un solo proyecto, pero lo dividiremos en módulos lógicos.

Componentes de la aplicación

Los elementos de nuestra aplicación se pueden dividir en varios bloques clave:

  1. REST API: vamos a ofrecer una interfaz de interacción vía HTTP requests para usuarios externos (por ejemplo, el frontend).
  2. Capa de servicio (business logic): aquí concentraremos la funcionalidad principal de la aplicación, como el procesamiento de datos, validación y gestión de transacciones.
  3. Base de datos: para el almacenamiento de la información.
  4. Seguridad: protegeremos el acceso a nuestros datos y funcionalidades.

Aquí tienes un esquema del componente:


+-------------------+
|   REST API        |
|-------------------|
|  Controladores    |
+-------------------+
        |
        v
+-------------------+
| Capa de servicio  |
|-------------------|
|  Lógica de negocio|
+-------------------+
        |
        v
+-------------------+
|   Acceso a datos  |
|-------------------|
|  Repositorios JPA |
+-------------------+
        |
        v
+-------------------+
|  Base de datos    |
|-------------------|

2. Diseño REST API

Para empezar, recordemos que REST API —es la interfaz que permite interactuar con la aplicación vía HTTP requests. Principios principales de REST:

  1. Orientación a recursos: todo en nuestra aplicación se considera un recurso. Por ejemplo, un usuario es el recurso /users.
  2. HTTP methods: para distintas operaciones se usan diferentes métodos:
    • GET para obtener datos.
    • POST para crear nuevos datos.
    • PUT para actualizar datos.
    • DELETE para eliminar datos.
  3. Códigos de estado HTTP: el servidor devuelve el estado de la operación. Por ejemplo:
    • 200 OK para respuesta exitosa.
    • 201 Created para creación exitosa del recurso.
    • 404 Not Found para recurso no encontrado.

Diseño de endpoints

Pensamos crear un CRUD básico para usuarios. Estos son los endpoints previstos:

Método URL Descripción
GET /users Obtener la lista de usuarios
GET /users/{id} Obtener un usuario por ID
POST /users Crear un nuevo usuario
PUT /users/{id} Actualizar los datos de un usuario
DELETE /users/{id} Eliminar un usuario

Así se ve en el controlador:


@RestController
@RequestMapping("/users")
public class UserController {

    @GetMapping
    public List<User> getAllUsers() {
        // Devuelve la lista de todos los usuarios
    }

    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        // Obtiene el usuario por id
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        // Crea un nuevo usuario
    }

    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        // Actualiza los datos del usuario
    }

    @DeleteMapping("/{id}")
    public ResponseEntity
    deleteUser(@PathVariable Long id) {
        // Elimina el usuario
    }
}

3. Seguridad de la aplicación

Implementación de autenticación usando Spring Security

Ya sabéis que Spring Security nos permite añadir reglas de autenticación y autorización.

1. Inclusión de la dependencia: En pom.xml añadimos:


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

2. Creación de la configuración de seguridad:


@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/users/**").authenticated()
            .anyRequest().permitAll()
            .and()
            .httpBasic(); // Usamos autenticación básica
    }
}

Aquí protegemos los endpoints /users/**, y dejamos el resto accesible para todos.


4. Trabajo con la base de datos

Diseño de la base de datos

Para nuestra aplicación necesitaremos la tabla users. Ejemplo de estructura (diagrama ER):

Campo Tipo de datos Descripción
id BIGINT Identificador
name VARCHAR Nombre de usuario
email VARCHAR Correo electrónico
password VARCHAR Contraseña

Implementación de entidades

Crearemos la entidad User usando anotaciones JPA:


@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false, unique = true)
    private String email;

    @Column(nullable = false)
    private String password;

    // Getters y setters
}

Repositorio para trabajar con la base

Usamos Spring Data JPA para operar con los datos:


@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
}

Ahora podemos realizar operaciones CRUD con los datos de usuarios a través de esta interfaz.

Configuración de la conexión

En application.properties configuramos la conexión a la base:


spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=update

Interacción entre componentes

Entonces, ¿cómo se conecta todo esto?

  1. La petición REST llega al controlador.
  2. El controlador llama al método correspondiente de la capa de servicio.
  3. La capa de servicio interactúa con los repositorios y devuelve el resultado.
  4. El controlador devuelve la respuesta al cliente.

Ejemplo de la cadena:


@RestController
public class UserController {

    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        return userService.getUserById(id);
    }
}

@Service
public class UserService {

    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User getUserById(Long id) {
        return userRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found"));
    }
}

En esta etapa la arquitectura de la aplicación está lista. A continuación nos meteremos en la descomposición de las tareas del proyecto para entender cómo organizar mejor el trabajo en los módulos restantes.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION