Граница приложения принимает данные
Контроллер связывает внешний запрос с внутренним действием. Здесь удобно преобразовать тело запроса в отдельную модель данных и проверить обязательность полей, длину строки или допустимый диапазон числа. Например, отрицательное количество единиц в запросе добавления товара противоречит самому формату операции. При этом корректный формат ещё не означает допустимость действия: существование товара и право изменять его требуют других проверок. Возвращаемая ошибка должна быть понятной клиенту и не раскрывать внутренние подробности приложения.
Бизнес-правило имеет собственное место
Прикладной сервис выражает действие: добавить позицию, изменить остаток или подтвердить заказ. Он координирует необходимые проверки и обращение к хранилищу. Если одно правило спрятано в нескольких контроллерах, оно легко расходится после изменений. Поэтому полезно сформулировать инвариант словами, например количество доступных единиц не становится отрицательным. Затем определить, где это условие обеспечивается, включая одновременные запросы. Простая предварительная проверка сама по себе не защищает от изменения данных другим исполнителем.
Хранение и транзакция решают разные задачи
Репозиторий предоставляет операции чтения и сохранения, скрывая часть технических деталей доступа к данным. Транзакция объединяет связанные изменения в определённую границу согласованности. Если операция обновляет остаток и записывает движение товара, частичное сохранение может нарушить смысл учёта. Однако наличие транзакции не означает автоматического решения всех конфликтов конкурентного доступа. Внешнее сообщение или сетевой вызов также не становятся частью отката базы только потому, что произошли внутри того же метода.
Тестируйте договорённости между частями
Проверка контроллера отвечает на вопрос о формате запроса и ответа. Проверка сервиса исследует предметные правила. Интеграционный тест показывает реальное взаимодействие с хранилищем и транзакциями. Эти уровни дополняют друг друга. Полезно включать не только успешное изменение, но и отсутствующий объект, нарушение правила, ошибку сохранения и повторный запрос. Идентификатор для сопоставления записей в журнале помогает проследить путь операции, но журнал не должен превращаться в бесконтрольное хранилище пользовательских данных.
Попробуйте на практике
На бумаге спроектируйте операцию уменьшения остатка в вымышленном учебном каталоге.
- Запишите входные поля и отдельно сформулируйте проверки формата: обязательность идентификатора и положительное количество.
- Опишите правило доступности остатка и распределите действия между контроллером, сервисом и репозиторием.
- Отметьте связанные изменения, которые должны завершиться совместно, и придумайте сбой между ними.
- Составьте четыре тестовых случая: успех, неверный формат, недостаточный остаток и два одновременных запроса на последние единицы.
Как проверить результат. Каждая проверка имеет владельца, ошибка не оставляет частичное изменение, а сценарий конкурентного доступа рассмотрен отдельно от обычной валидации.
Частые вопросы
Нужен ли отдельный класс для каждого слоя всегда?
Нет. Разделение должно помогать пониманию и проверке. Для маленькой задачи важнее ясные границы ответственности, чем большое число пустых обёрток.
Заменяет ли проверка входных полей ограничения базы?
Нет. Они работают на разных границах. Проверка запроса улучшает обратную связь, а ограничения хранилища помогают сохранять допустимое состояние при разных путях записи.
Самостоятельный разбор темы. Содержание конкретной обучающей программы здесь не представлено.