Curso de Spring Boot | 5. Implementación de servicios

Por 9.99€ al mes tendrás acceso completo a todos los cursos. Sin matrícula ni permanencia.

Aunque no es indispensable, la implementación de servicios en un proyecto de SpringBoot es habitual y tiene las siguientes ventajas:

  • Separación de responsabilidades: Mantener la lógica de negocio separada de la capa de controladores.
  • Reutilización: Facilitar la reutilización de la lógica de negocio en diferentes partes de la aplicación.
  • Mantenimiento: Hacer que el código sea más fácil de mantener y probar.

En SpringBoot lo servicios son clases que generalmente son llamadas desde los controladores, aunque también pueden ser llamadas desde otros servicios o componentes.

A menudo, los servicios se encargan de mapear los DTO a nuestras entidades del modelo y viceversa.

package com.app.controllers;

import com.app.services.BookServices;

@RestController
@CrossOrigin
@RequestMapping("/locations")
public class BookController {
    @Autowired
    LocationServices locationServices;

 @PostMapping
    public ResponseEntity<?> saveBook(@RequestBody LocationTagDTO locationTagDTO) {
        try {
            BookDTO saveLocation = bookServices.createBook(bookDTO);
            return ResponseEntity.status(200).body(saveLocation);
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.status(500).body("Error al crear la ubicación " + e.getMessage());
        }
    }
package com.app.services.impl
...
@Service
public class BookServicesImpl implements BookServices {

    @Autowired
    private BookRepository bookRepository;

    @Override
    public BookDTO createBook(BookDTO bookDTO) {
        Book book = new Book();
        book.setTitle(bookDTO.getTitle());

        bookRepository.save(location);

        // Mapear la entidad de vuelta a un DTO para devolverlo (best practices)
        BookDTO bookDTO = new BookDTO();
        bookDTO.setTitle(book.getTitle());
        return bookDTO;
    }
}

Usamos la siguiente interfaz para definir un contrato que la clase BookServicesImpl debe cumplir. Esto permite:

  1. Abstracción: Ocultar los detalles de implementación y exponer solo los métodos necesarios.
  2. Flexibilidad: Facilitar el cambio de implementación sin afectar a las clases que dependen de la interfaz.
  3. Testabilidad: Permitir el uso de mocks o stubs en pruebas unitarias.

com.pablomonteserin.prueba.services

public interface BookServices {
    BookDTO createLocation (BookDTO bookDTO);
}

Mapeos automáticos

El mapeo que vimos antes para pasar de DTO a Entity se podía haber hecho automáticamente con un mapper.

Para ello, será necesario cargar las siguientes dependencias en el pom.xml:

<!-- MapStruct-->
<dependency>
	<groupId>org.mapstruct</groupId>
	<artifactId>mapstruct</artifactId>
	<version>1.6.3</version>
</dependency>
<dependency>
	<groupId>org.mapstruct</groupId>
	<artifactId>mapstruct-processor</artifactId>
	<version>1.6.3</version>
</dependency>
package com.app.mapper;
...
@Mapper(componentModel = "spring")
public interface UserMapper {
    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    @Mapping(target = "password", expression = "java(passwordEncoderService.encodePassword(source.getPassword()))")
    User fromUserDTO(UserDTO source);

    @Mapping(source = "owner.id", target = "ownerId")
    @Mapping(target = "password", ignore = true)
    UserDTO fromUser(User source);
}

Por 9.99€ al mes tendrás acceso completo a todos los cursos. Sin matrícula ni permanencia.