Архитектура приложения: слои и поток запроса
Приложение на Spring Boot построено слоями: каждый слой отвечает за свою задачу и общается только с соседом. Это и есть основа поддерживаемого кода.
Суть: запрос проходит через DispatcherServlet к контроллеру, контроллер зовёт сервис (бизнес-логика), сервис — репозиторий (доступ к данным), репозиторий — базу. Ответ возвращается тем же путём наружу.
Почему вообще нужны слои? Представьте, что вся логика свалена в один класс: и разбор HTTP, и расчёты, и SQL-запросы. Такой код невозможно тестировать и страшно менять — правка в одном месте ломает три других. Слоистая архитектура разделяет ответственность: каждый слой знает только то, что ему нужно, и заменяем независимо.
Четыре слоя
Контроллер (Controller) — «дверь» приложения. Он принимает HTTP-запрос, достаёт параметры и тело, вызывает сервис и формирует HTTP-ответ. Он не считает и не ходит в базу — только переводит между миром HTTP и миром Java.
Сервис (Service) — мозг приложения. Здесь живёт бизнес-логика: проверки правил, расчёты, координация нескольких репозиториев, транзакции. Сервис работает с сущностями и ничего не знает про HTTP.
Репозиторий (Repository) — слой доступа к данным. Он умеет сохранять, искать и удалять записи. В Spring Data JPA вы даже не пишете SQL — достаточно объявить интерфейс.
База данных — постоянное хранилище. Обычно PostgreSQL или MySQL.
Как работает под капотом: поток запроса
Когда приходит HTTP-запрос, его первым встречает DispatcherServlet — центральный диспетчер Spring MVC. Он смотрит на URL и метод (GET/POST), находит нужный метод контроллера через HandlerMapping и передаёт ему управление. Дальше запрос идёт вглубь по слоям, а ответ — наружу.
Клиент (браузер / фронтенд)
| HTTP-запрос GET /api/users/7
v
+-------------------+
| DispatcherServlet | выбирает обработчик по URL
+-------------------+
|
v
+-------------------+
| Controller | разбирает запрос, зовёт сервис
+-------------------+
|
v
+-------------------+
| Service | бизнес-логика, транзакции
+-------------------+
|
v
+-------------------+
| Repository | доступ к данным (JPA)
+-------------------+
|
v
+-------------------+
| База данных | SELECT ... WHERE id = 7
+-------------------+
|
v ответ поднимается обратно тем же путём
JSON {"id":7,"name":"Анна"} --> Клиент
Обратите внимание: данные не «перепрыгивают» через слой. Контроллер не лезет в базу напрямую, а сервис не формирует JSON. Каждый слой говорит только с соседом — это правило держит код в порядке.
Смоделируем поток запроса на простом «языко-независимом» примере, который показывает идею делегирования по слоям:
# Аналог потока запроса: каждый слой делегирует соседу
db = {7: {"id": 7, "name": "Анна"}}
def repository(user_id):
return db.get(user_id) # доступ к данным
def service(user_id):
user = repository(user_id) # бизнес-логика
if user is None:
raise ValueError("Пользователь не найден")
return user
def controller(request_id):
try:
user = service(request_id) # разбор запроса
return {"status": 200, "body": user}
except ValueError as e:
return {"status": 404, "body": {"error": str(e)}}
print(controller(7)) # 200, найден
print(controller(99)) # 404, не найден
Запустите этот код кнопкой «Попробуй сам ▶». Это та же логика, что во взрослом приложении: контроллер ловит ошибку из сервиса и превращает её в HTTP-статус.
Частые ошибки
- SQL в контроллере. Когда контроллер сам ходит в базу, слои смешиваются и тестировать становится невозможно.
- Бизнес-логика в контроллере. Контроллер должен быть тонким: разобрал запрос — делегировал сервису.
- Возврат сущностей наружу. Сущность БД не должна напрямую улетать в JSON-ответ — для этого есть DTO (отдельный раздел).
Best practices
- Держите контроллеры тонкими, сервисы — с логикой, репозитории — только с доступом к данным.
- Раскладывайте код по пакетам
controller,service,repository,dto,entity. - Никогда не позволяйте слою перепрыгивать через соседа.
Итог: слоистая архитектура — фундамент Spring Boot. Запрос проходит DispatcherServlet → Controller → Service → Repository → БД и возвращается обратно. Запомните этот путь — мы будем возвращаться к нему весь курс.