Лабораторная работа 4: Реализация Service и Controller слоев в Spring Boot приложении с PostgreSQL

1. Создание Service слоя

  1. Базовый сервис для сущности User
    
    public interface UserService {
        UserDTO createUser(UserCreateDTO dto);
        UserDTO getUserById(Long id);
        List<UserDTO> getAllUsers();
        UserDTO updateUser(Long id, UserUpdateDTO dto);
        void deleteUser(Long id);
    }
        
  2. Реализация сервиса
    
    @Service
    @RequiredArgsConstructor
    public class UserServiceImpl implements UserService {
        private final UserRepository userRepository;
        private final UserMapper userMapper;
    }
        
  3. Метод создания сущности
    
    @Override
    @Transactional
    public UserDTO createUser(UserCreateDTO dto) {
        User user = userMapper.fromCreateDto(dto);
        User saved = userRepository.save(user);
        return userMapper.toDto(saved);
    }
        
  4. Обработка ошибок
    
    @Override
    public UserDTO getUserById(Long id) {
        return userRepository.findById(id)
            .map(userMapper::toDto)
            .orElseThrow(() -> new ResourceNotFoundException("User not found"));
    }
        
  5. Транзакционные методы
    
    @Transactional(readOnly = true)
    @Override
    public List<UserDTO> getAllUsers() {
        return userRepository.findAll()
            .stream()
            .map(userMapper::toDto)
            .toList();
    }
        
  6. Метод обновления
    
    @Override
    @Transactional
    public UserDTO updateUser(Long id, UserUpdateDTO dto) {
        User user = userRepository.findById(id)
            .orElseThrow(() -> new ResourceNotFoundException("User not found"));
    
        userMapper.updateFromDto(dto, user);
        User updated = userRepository.save(user);
        return userMapper.toDto(updated);
    }
        
  7. Логирование в сервисе
    
    @Slf4j
    @Service
    public class UserServiceImpl implements UserService {
        @Override
        public UserDTO createUser(...) {
            log.info("Creating user with email: {}", dto.getEmail());
            // ...
        }
    }
        
  8. Кеширование
    
    @Cacheable(value = "users", key = "#id")
    @Override
    public UserDTO getUserById(Long id) {
        ...
    }
        
  9. Валидация бизнес-логики
    
    if (userRepository.existsByEmail(dto.getEmail())) {
        throw new BusinessException("Email already exists");
    }
        
  10. Пагинация в сервисе
    
    @Override
    public Page<UserDTO> getUsersPage(Pageable pageable) {
        return userRepository.findAll(pageable)
            .map(userMapper::toDto);
    }
        
  11. Специфическая бизнес-логика
    
    @Override
    public void deactivateUser(Long id) {
        User user = userRepository.findById(id)
            .orElseThrow(...);
        user.setActive(false);
        userRepository.save(user);
        sendDeactivationEmail(user.getEmail());
    }
        
  12. Интеграция с другими сервисами
    
    private final EmailService emailService;
    private final AuditService auditService;
        

2. Создание Controller слоя

  1. Базовый REST-контроллер
    
    @RestController
    @RequestMapping("/api/v1/users")
    @RequiredArgsConstructor
    public class UserController {
        private final UserService userService;
    }
        
  2. Метод создания (POST)
    
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public UserDTO createUser(@Valid @RequestBody UserCreateDTO dto) {
        return userService.createUser(dto);
    }
        
  3. Получение по ID (GET)
    
    @GetMapping("/{id}")
    public UserDTO getUser(@PathVariable Long id) {
        return userService.getUserById(id);
    }
        
  4. Получение всех (с фильтрацией)
    
    @GetMapping
    public List<UserDTO> getAllUsers(@RequestParam(required = false) String emailFilter) {
        return userService.getAllUsers(emailFilter);
    }
        
  5. Обновление (PUT)
    
    @PutMapping("/{id}")
    public UserDTO updateUser(@PathVariable Long id, @Valid @RequestBody UserUpdateDTO dto) {
        return userService.updateUser(id, dto);
    }
        
  6. Удаление (DELETE)
    
    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
        
  7. Обработка исключений
    
    @ExceptionHandler(ResourceNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ErrorResponse handleNotFound(ResourceNotFoundException ex) {
        return new ErrorResponse(ex.getMessage());
    }
        
  8. Валидация запросов
    
    public class UserCreateDTO {
        @NotBlank
        @Size(max = 100)
        private String name;
        
        @Email
        private String email;
    }
        
  9. Документирование API (Swagger)
    
    @Operation(summary = "Create new user")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "201", description = "User created"),
        @ApiResponse(responseCode = "400", description = "Invalid input")
    })
    @PostMapping
    public UserDTO createUser(...) { ... }
        
  10. Пагинация в контроллере
    
    @GetMapping("/page")
    public Page<UserDTO> getUsersPage(
            @PageableDefault(sort = "name", direction = Sort.Direction.ASC) Pageable pageable) {
        return userService.getUsersPage(pageable);
    }
        
  11. Версионирование API
    
    @RestController
    @RequestMapping("/api/v2/users")
    public class UserControllerV2 extends UserController {
        // Переопределенные методы
    }
        
  12. Аутентификация в методах
    
    @PreAuthorize("hasRole('ADMIN')")
    @DeleteMapping("/{id}")
    public void deleteUser(...) { ... }
        
  13. Кастомные HTTP-заголовки
    
    @GetMapping("/{id}")
    public ResponseEntity<UserDTO> getUserWithHeaders(@PathVariable Long id) {
        UserDTO dto = userService.getUserById(id);
        return ResponseEntity.ok()
            .header("X-Custom-Header", "value")
            .body(dto);
    }
        

Пример интеграции


@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
@Tag(name = "User Management", description = "Operations with users")
public class UserController {
    private final UserService userService;

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    @Operation(summary = "Create new user")
    public UserDTO createUser(@Valid @RequestBody UserCreateDTO dto) {
        return userService.createUser(dto);
    }

    @GetMapping("/{id}")
    @Operation(summary = "Get user by ID")
    public UserDTO getUser(@PathVariable Long id) {
        return userService.getUserById(id);
    }
}

Вывод

В ходе работы были реализованы все ключевые компоненты сервисного и контроллерного слоев, включая обработку запросов, валидацию, пагинацию и обработку ошибок. Полученная структура соответствует best practices REST API разработки на Spring Boot.