← Назад к вопросам

На какой слой идет обращение после контроллера при создании MVC приложения

1.8 Middle🔥 181 комментариев
#Docker, Kubernetes и DevOps#JVM и управление памятью#ORM и Hibernate

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

На какой слой идет обращение после контроллера при создании MVC приложения

Краткий ответ

После контроллера обращение идет на Service слой (бизнес-логика). Это стандартный поток данных в MVC архитектуре:

Controller → Service → Repository → Database

Архитектура MVC с слоями

┌─────────────────────────────────────────────┐
│         Presentation Layer (UI)             │
│    (HTML, JavaScript, JSON responses)       │
└──────────────────┬──────────────────────────┘
                   │ HTTP Request/Response
┌──────────────────▼──────────────────────────┐
│      Controller Layer                       │
│  (Получает запрос, валидация)               │
└──────────────────┬──────────────────────────┘
                   │ вызов методов
┌──────────────────▼──────────────────────────┐
│      Service Layer (Business Logic)         │
│  (Обработка, вычисления, правила)           │
└──────────────────┬──────────────────────────┘
                   │ работа с данными
┌──────────────────▼──────────────────────────┐
│      Repository/DAO Layer                   │
│  (Доступ к данным, SQL запросы)             │
└──────────────────┬──────────────────────────┘
                   │
┌──────────────────▼──────────────────────────┐
│      Database                               │
│  (PostgreSQL, MySQL, MongoDB)               │
└─────────────────────────────────────────────┘

Практический пример: User Management

1. HTTP запрос приходит в контроллер:

@RestController
@RequestMapping("/api/v1/users")
public class UserController {
    
    private final UserService userService;  // Инъекция Service
    
    // POST /api/v1/users
    @PostMapping
    public ResponseEntity<UserDTO> createUser(@RequestBody CreateUserRequest request) {
        // Контроллер: валидация базовых параметров
        if (request.getEmail() == null) {
            return ResponseEntity.badRequest().build();
        }
        
        // Контроллер: ПЕРЕДАЧА контроля Service слою
        User createdUser = userService.createUser(request);
        
        // Контроллер: преобразование ответа и возврат
        UserDTO response = mapToDTO(createdUser);
        return ResponseEntity.status(HttpStatus.CREATED).body(response);
    }
}

2. Service слой содержит бизнес-логику:

@Service
public class UserService {
    
    private final UserRepository userRepository;
    private final EmailService emailService;
    private final PasswordEncoder passwordEncoder;
    
    @Transactional
    public User createUser(CreateUserRequest request) {
        // Service: проверка бизнес-правил
        if (userRepository.existsByEmail(request.getEmail())) {
            throw new UserAlreadyExistsException("Email already registered");
        }
        
        // Service: обработка данных
        User user = new User();
        user.setEmail(request.getEmail());
        user.setPassword(passwordEncoder.encode(request.getPassword()));
        user.setCreatedAt(LocalDateTime.now(UTC));
        
        // Service: обращение к Repository (доступ к БД)
        User savedUser = userRepository.save(user);
        
        // Service: дополнительные операции
        emailService.sendWelcomeEmail(savedUser.getEmail());
        
        // Service: возврат результата
        return savedUser;
    }
}

3. Repository слой работает с данными:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    
    // Repository: доступ к БД
    Optional<User> findByEmail(String email);
    
    boolean existsByEmail(String email);
    
    // Spring Data JPA генерирует SQL запросы автоматически
    // SELECT * FROM users WHERE email = ?
}

Полный пример: User Registration

// 1. CONTROLLER LAYER
@RestController
@RequestMapping("/api/v1/auth")
public class AuthController {
    
    private final AuthService authService;
    
    @PostMapping("/register")
    public ResponseEntity<AuthResponse> register(@RequestBody RegisterRequest request) {
        // Контроллер: базовая валидация
        if (request.getPassword().length() < 8) {
            return ResponseEntity.badRequest().build();
        }
        
        // Контроллер: передача контроля Service
        AuthResponse response = authService.register(request);
        
        return ResponseEntity.ok(response);
    }
}

// 2. SERVICE LAYER (Бизнес-логика)
@Service
public class AuthService {
    
    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;
    private final JwtTokenProvider jwtTokenProvider;
    
    @Transactional
    public AuthResponse register(RegisterRequest request) {
        // Service: проверка бизнес-правил
        if (userRepository.existsByEmail(request.getEmail())) {
            throw new EmailAlreadyRegisteredException();
        }
        
        // Service: обработка данных
        User user = new User();
        user.setEmail(request.getEmail());
        user.setPassword(passwordEncoder.encode(request.getPassword()));
        user.setName(request.getName());
        user.setEnabled(false);
        
        // Service: обращение к Repository
        User savedUser = userRepository.save(user);
        
        // Service: создание токена
        String token = jwtTokenProvider.generateToken(savedUser);
        
        // Service: возврат ответа
        return new AuthResponse(token, savedUser.getEmail());
    }
}

// 3. REPOSITORY LAYER (Доступ к данным)
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
    boolean existsByEmail(String email);
    
    // Spring Data JPA генерирует SQL:
    // SELECT * FROM users WHERE email = ?
    // INSERT INTO users (...) VALUES (...)
}

Разделение ответственности

Что делает CONTROLLER:

  • ✅ Получение HTTP запроса
  • ✅ Парсинг параметров и body
  • ✅ Базовая валидация (null, empty)
  • ✅ Вызов методов Service
  • ✅ Форматирование ответа
  • ✅ Возврат HTTP response

ЧТО ДЕЛАЕТ SERVICE (БИЗНЕС-ЛОГИКА):

  • ✅ Проверка бизнес-правил
  • ✅ Сложная валидация
  • ✅ Обработка и преобразование данных
  • ✅ Кэширование
  • ✅ Координация между Repository и другими Service
  • ✅ Транзакции (@Transactional)
  • ✅ Обработка ошибок

Что делает REPOSITORY:

  • ✅ Выполнение SQL запросов
  • ✅ Маппинг результатов в объекты
  • ✅ Работа с ORM (Hibernate, JPA)

Антипаттерны

❌ ПЛОХО: бизнес-логика в контроллере

@RestController
public class BadController {
    
    @Autowired
    private UserRepository userRepository;
    
    @PostMapping("/register")
    public ResponseEntity<?> register(@RequestBody RegisterRequest request) {
        // ❌ Бизнес-логика в контроллере!
        if (userRepository.existsByEmail(request.getEmail())) {
            throw new Exception("Email exists");
        }
        
        // ❌ Работа с БД напрямую
        User user = new User();
        user.setEmail(request.getEmail());
        userRepository.save(user);
        
        // Проблемы:
        // - Контроллер перегружен
        // - Невозможно переиспользовать логику
        // - Сложно тестировать
        // - Нарушен SOLID принцип
    }
}

✅ ХОРОШО: разделение на слои

@RestController
public class GoodController {
    
    private final UserService userService;  // Зависимость
    
    @PostMapping("/register")
    public ResponseEntity<AuthResponse> register(@RequestBody RegisterRequest request) {
        // Контроллер: только валидация и передача контроля
        AuthResponse response = userService.register(request);
        return ResponseEntity.ok(response);
    }
}

@Service
public class GoodService {
    
    private final UserRepository userRepository;
    
    @Transactional
    public AuthResponse register(RegisterRequest request) {
        // Service: вся бизнес-логика
        validateEmail(request.getEmail());
        User user = createUserEntity(request);
        userRepository.save(user);
        return buildResponse(user);
    }
    
    // Переиспользуемые методы
    private void validateEmail(String email) { }
    private User createUserEntity(RegisterRequest request) { }
    private AuthResponse buildResponse(User user) { }
}

Где обрабатываются Exception?

// Исключения могут выбрасываться на любом слое

// Service
public User createUser(CreateUserRequest request) throws UserAlreadyExistsException {
    if (userRepository.existsByEmail(request.getEmail())) {
        throw new UserAlreadyExistsException("Email already exists");
    }
    // ...
}

// Controller обрабатывает exception
@RestController
public class UserController {
    
    @ExceptionHandler(UserAlreadyExistsException.class)
    public ResponseEntity<ErrorResponse> handleUserExists(UserAlreadyExistsException e) {
        return ResponseEntity.badRequest()
            .body(new ErrorResponse("CONFLICT", e.getMessage()));
    }
}

MVC + Clean Architecture

Современный подход добавляет ещё больше слоёв:

┌───────────────────────────────────┐
│  Controller (HTTP Interface)      │
└───────────────────┬───────────────┘
                    │ DTO
┌───────────────────▼───────────────┐
│  UseCase / Application Service    │
│  (Случаи использования)           │
└───────────────────┬───────────────┘
                    │
┌───────────────────▼───────────────┐
│  Domain Service (Бизнес-правила)  │
│  (Entities, Aggregates)           │
└───────────────────┬───────────────┘
                    │
┌───────────────────▼───────────────┐
│  Repository (Data Access)         │
│  (Interface)                      │
└───────────────────┬───────────────┘
                    │
┌───────────────────▼───────────────┐
│  Infrastructure (БД, API, File)   │
└───────────────────────────────────┘

Таблица: Что на каком слое

ЗадачаСлойПример
Парсинг HTTPController@RequestBody, @PathVariable
Валидация параметровControllernull check, format check
Бизнес-логикаServiceесли email существует, выбросить ошибку
Работа с БДRepositorySELECT, INSERT, UPDATE
КэшированиеService@Cacheable
БезопасностьController, Service@PreAuthorize, roles
ЛогированиеВсе@Slf4j
ТранзакцииService@Transactional

Заключение

Поток данных в MVC:

  1. Controller получает HTTP запрос
  2. Controller валидирует параметры
  3. Controller вызывает Service
  4. Service содержит бизнес-логику
  5. Service вызывает Repository
  6. Repository работает с БД
  7. Repository возвращает результаты в Service
  8. Service обрабатывает и возвращает в Controller
  9. Controller форматирует ответ и возвращает HTTP response

Запомните: Controller → Service → Repository → Database