← назад к методологии · уровень 1 из 3
Почти каждый Spring-проект начинается одинаково: контроллер вызывает сервис, сервис идёт в репозиторий. Эту схему называют слоёной архитектурой. Она простая, понятная и в большинстве случаев работает отлично — до тех пор, пока сервис не разрастается до полусотни методов.
Что такое слоёная архитектура
Три слоя, выстроенных в цепочку:
HTTP-запрос
↓
Controller — принимает запрос, зовёт сервис, возвращает ответ
↓
Service — бизнес-логика, проверки, оркестрация
↓
Repository — запросы в базу данных
Каждый слой отвечает за своё. Контроллер не лезет в базу — это дело репозитория. Репозиторий не принимает HTTP — это дело контроллера. Сервис в середине держит логику.
Код выглядит примерно так:
@RestController
class OrderController {
private final OrderService service;
@PostMapping("/orders")
OrderResponse create(@RequestBody CreateOrderRequest req) {
return service.createOrder(req);
}
}
@Service
class OrderService {
private final OrderRepository repo;
@Transactional
OrderResponse createOrder(CreateOrderRequest req) {
// проверки, логика, сохранение
Order order = new Order(req.getCustomerId(), req.getItems());
repo.save(order);
return OrderResponse.from(order);
}
}
Никаких специальных абстракций — только три класса и их взаимодействие.
Когда этого достаточно
Слоёная архитектура отлично подходит для:
- Настоящего CRUD. Справочники, словари, простые административные панели. Операции — создать, прочитать, изменить, удалить. Никакой сложной логики внутри.
- Прототипа или короткоживущего сервиса. Если сервис проживёт год-два и потом будет переписан или выключен — вкладываться в сложную структуру не имеет смысла.
- Маленькой команды с понятным доменом. Когда домен укладывается в голове за полчаса, а инвариантов почти нет, дополнительные абстракции только мешают.
- Тонкого прокси. Сервис принимает запрос и перекладывает его в другой сервис или внешнее API — бизнес-логики минимум.
Уровень 1 — не «недоделанный» уровень. Для части сервисов он остаётся достаточным навсегда. Добавлять сложность ради сложности — ошибка.
Что должно быть на этом уровне
Тонкие контроллеры. Контроллер делает ровно одно: принимает запрос, зовёт сервис, возвращает ответ. Никакой логики внутри контроллера.
Сервисы по предметным областям. OrderService, CustomerService — каждый отвечает за свою тему. Логика и обращения к репозиторию живут внутри сервиса.
Транзакция на уровне метода. Одна операция — один @Transactional метод. Либо всё сохраняется, либо ничего.
Одна модель данных. То, что приходит из API, и то, что лежит в базе, связано простым маппингом. Отдельных доменных моделей и объектов-значений на этом уровне нет — они появятся выше.
Где это начинает мешать
Слоёная модель хороша до тех пор, пока сервис остаётся небольшим. Проблемы начинаются, когда он растёт:
Сервис обрастает десятками методов. OrderService с тридцатью методами — это уже не очевидная структура. Непонятно, какие из них реальные бизнес-операции, а какие — вспомогательные. Список операций приходится собирать вручную, просматривая сигнатуры.
Одна операция вызывается из нескольких мест с отличиями. Если одну операцию вызывают из трёх контроллеров с чуть разными проверками, эти проверки со временем расходятся — появляются незаметные ошибки.
Нет единой точки контроля. Метрики на операцию, аудит, единый способ обработки ошибок — всё это приходится добавлять в каждый метод руками. Легко пропустить, сложно поддерживать.
Сложно тестировать операцию изолированно. Тест бизнес-операции тянет за собой весь сервис-класс со всеми зависимостями. Нет способа запустить только одну операцию без остальных.
Это не «плохой код» — это предел слоёной модели. У неё просто нет механизма явно выделять отдельные операции.
Что фиксируется в спецификации на этом уровне
Если проект использует Use Case спецификацию, на уровне 1 она минимальна: глоссарий, роли, схема данных и список операций. Агрегатов и доменных событий нет — они помечаются «не применимо на Уровне 1». Центральная сущность всё равно выносится в отдельный файл — формат раскладки единый для всех уровней.
Признаки, что пора на уровень 2
- Сервис-классы разрослись, и операции в них не различить.
- Хочется на каждую операцию автоматические метрики, аудит, единый способ обработки ошибок.
- Появилась потребность тестировать одну бизнес-операцию изолированно, без запуска всего сервиса.
Если хотя бы одно из этого актуально — пора к следующему шагу: Уровень 2: Use Case Pattern. Там каждая бизнес-операция выделяется в отдельный UseCase + Handler.
Коротко
- Слоёная архитектура — три слоя: Controller, Service, Repository.
- Подходит для CRUD, прототипов, маленьких сервисов с простым доменом.
- Контроллер тонкий, логика в сервисе, транзакция на уровне метода.
- Ломается, когда сервис разрастается: теряется явность операций, метрики и аудит не масштабируются.
- Уровень 1 — осознанный выбор, а не недоделка. Для части сервисов он достаточен навсегда.
Что почитать дальше
- Use Case Pattern — все три уровня — общая картина и когда переходить между уровнями.
- Уровень 2: Use Case Pattern — явное выделение каждой операции в
UseCase+Handler. - REST API Style Guide — контракты для внешнего API.