Лабораторная работа 4: Реализация Service и Controller слоев в Spring Boot приложении с PostgreSQL
1. Создание Service слоя
- Базовый сервис для сущности 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); } - Реализация сервиса
@Service @RequiredArgsConstructor public class UserServiceImpl implements UserService { private final UserRepository userRepository; private final UserMapper userMapper; } - Метод создания сущности
@Override @Transactional public UserDTO createUser(UserCreateDTO dto) { User user = userMapper.fromCreateDto(dto); User saved = userRepository.save(user); return userMapper.toDto(saved); } - Обработка ошибок
@Override public UserDTO getUserById(Long id) { return userRepository.findById(id) .map(userMapper::toDto) .orElseThrow(() -> new ResourceNotFoundException("User not found")); } - Транзакционные методы
@Transactional(readOnly = true) @Override public List<UserDTO> getAllUsers() { return userRepository.findAll() .stream() .map(userMapper::toDto) .toList(); } - Метод обновления
@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); } - Логирование в сервисе
@Slf4j @Service public class UserServiceImpl implements UserService { @Override public UserDTO createUser(...) { log.info("Creating user with email: {}", dto.getEmail()); // ... } } - Кеширование
@Cacheable(value = "users", key = "#id") @Override public UserDTO getUserById(Long id) { ... } - Валидация бизнес-логики
if (userRepository.existsByEmail(dto.getEmail())) { throw new BusinessException("Email already exists"); } - Пагинация в сервисе
@Override public Page<UserDTO> getUsersPage(Pageable pageable) { return userRepository.findAll(pageable) .map(userMapper::toDto); } - Специфическая бизнес-логика
@Override public void deactivateUser(Long id) { User user = userRepository.findById(id) .orElseThrow(...); user.setActive(false); userRepository.save(user); sendDeactivationEmail(user.getEmail()); } - Интеграция с другими сервисами
private final EmailService emailService; private final AuditService auditService;
2. Создание Controller слоя
- Базовый REST-контроллер
@RestController @RequestMapping("/api/v1/users") @RequiredArgsConstructor public class UserController { private final UserService userService; } - Метод создания (POST)
@PostMapping @ResponseStatus(HttpStatus.CREATED) public UserDTO createUser(@Valid @RequestBody UserCreateDTO dto) { return userService.createUser(dto); } - Получение по ID (GET)
@GetMapping("/{id}") public UserDTO getUser(@PathVariable Long id) { return userService.getUserById(id); } - Получение всех (с фильтрацией)
@GetMapping public List<UserDTO> getAllUsers(@RequestParam(required = false) String emailFilter) { return userService.getAllUsers(emailFilter); } - Обновление (PUT)
@PutMapping("/{id}") public UserDTO updateUser(@PathVariable Long id, @Valid @RequestBody UserUpdateDTO dto) { return userService.updateUser(id, dto); } - Удаление (DELETE)
@DeleteMapping("/{id}") @ResponseStatus(HttpStatus.NO_CONTENT) public void deleteUser(@PathVariable Long id) { userService.deleteUser(id); } - Обработка исключений
@ExceptionHandler(ResourceNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public ErrorResponse handleNotFound(ResourceNotFoundException ex) { return new ErrorResponse(ex.getMessage()); } - Валидация запросов
public class UserCreateDTO { @NotBlank @Size(max = 100) private String name; @Email private String email; } - Документирование 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(...) { ... } - Пагинация в контроллере
@GetMapping("/page") public Page<UserDTO> getUsersPage( @PageableDefault(sort = "name", direction = Sort.Direction.ASC) Pageable pageable) { return userService.getUsersPage(pageable); } - Версионирование API
@RestController @RequestMapping("/api/v2/users") public class UserControllerV2 extends UserController { // Переопределенные методы } - Аутентификация в методах
@PreAuthorize("hasRole('ADMIN')") @DeleteMapping("/{id}") public void deleteUser(...) { ... } - Кастомные 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.