← Назад к вопросам
На какой слой идет обращение после контроллера при создании 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) │
└───────────────────────────────────┘
Таблица: Что на каком слое
| Задача | Слой | Пример |
|---|---|---|
| Парсинг HTTP | Controller | @RequestBody, @PathVariable |
| Валидация параметров | Controller | null check, format check |
| Бизнес-логика | Service | если email существует, выбросить ошибку |
| Работа с БД | Repository | SELECT, INSERT, UPDATE |
| Кэширование | Service | @Cacheable |
| Безопасность | Controller, Service | @PreAuthorize, roles |
| Логирование | Все | @Slf4j |
| Транзакции | Service | @Transactional |
Заключение
Поток данных в MVC:
- Controller получает HTTP запрос
- Controller валидирует параметры
- Controller вызывает Service
- Service содержит бизнес-логику
- Service вызывает Repository
- Repository работает с БД
- Repository возвращает результаты в Service
- Service обрабатывает и возвращает в Controller
- Controller форматирует ответ и возвращает HTTP response
Запомните: Controller → Service → Repository → Database