Агрегатор онлайн-сервисов, провайдеров и тарифов
4 798 Провайдеров
4 097 Сервисов в каталоге

Блог ДоверьсяСервису

MVC: как работает Model View Controller на практике

MVC, или Model View Controller, — архитектурный паттерн, который разделяет работу пользовательского интерфейса на три роли: модель отвечает за состояние и правила предметной области, представление показывает результат, контроллер принимает пользовательское действие и координирует ответ. Такое разделение помогает менять интерфейс, не переписывая бизнес-логику целиком.

MVC: как работает Model View Controller на практике

MVC, или Model View Controller, — архитектурный паттерн, который разделяет работу пользовательского интерфейса на три роли: модель отвечает за состояние и правила предметной области, представление показывает результат, контроллер принимает пользовательское действие и координирует ответ. Такое разделение помогает менять интерфейс, не переписывая бизнес-логику целиком.

Коротко: Model знает, что означают данные и какие операции допустимы; View знает, как показать подготовленные данные; Controller знает, как связать запрос пользователя с нужным сценарием. MVC — это распределение ответственности, а не обязательные три папки.

Зачем нужен MVC

В небольшом скрипте можно прочитать запрос, обратиться к базе и вывести HTML. При росте приложения смешиваются SQL, права, расчеты, шаблон и ошибки. Изменение верстки затрагивает правила, а тест требует запуска интерфейса.

MVC вводит границы. Пользовательский ввод обрабатывается отдельно от доменных правил, а подготовка HTML — отдельно от изменения состояния. Это не устраняет сложность, но делает ее видимой и позволяет проверять части системы независимо.

Три компонента MVC

КомпонентОсновная ответственностьЧего в нем быть не должно
ModelСостояние, бизнес-правила, операции предметной областиHTML, детали HTTP и выбор экрана
ViewПредставление подготовленных данных пользователюЗапись в базу и критичные бизнес-решения
ControllerПрием действия, вызов сценария, выбор результатаБольшие расчеты и прямое смешение всех зависимостей

Фреймворк добавляет роутер, middleware, binding, сервисы, репозитории и контейнер зависимостей. Они выполняют технические задачи вокруг трех ролей.

Что такое Model

Model — не обязательно одна таблица базы и не просто объект с полями. В широком смысле модель представляет состояние приложения и правила, по которым оно меняется. Например, заказ умеет добавлять допустимую позицию, считать сумму по утвержденной политике и запрещать изменение после закрытия.

Модель может включать сущности, объекты-значения и доменные сервисы. ORM-класс иногда играет роль модели данных, но это решение фреймворка, а не определение MVC.

Что такое View

View превращает подготовленное состояние в интерфейс: HTML, экран приложения, документ или другой формат. Для веб-сайта это часто шаблон, который получает ViewModel и выводит заголовок, список, цену, сообщение об ошибке и ссылки.

Условие отображения допустимо: например, показать кнопку только при canEdit. Но само право редактирования должно быть вычислено вне шаблона. Если View делает запросы к базе, рассчитывает скидку или меняет заказ, разделение ответственности нарушено.

Что такое Controller

Controller — входная точка пользовательского сценария на уровне интерфейса. Он получает уже разобранные параметры, проверяет форму запроса, вызывает модель или прикладной сервис и возвращает результат: View, перенаправление, JSON, файл либо код ошибки.

Контроллер не обязан знать SQL, доставку писем и расчет комиссии. Его код читается как план сценария. Множество бизнес-веток указывает на «толстый контроллер».

Как проходит HTTP-запрос

  1. Браузер отправляет GET /products/42.
  2. Роутер сопоставляет URL и HTTP-метод с действием контроллера.
  3. Middleware выполняет общие проверки, например сессию или ограничения.
  4. Controller получает идентификатор и вызывает сценарий чтения товара.
  5. Model или прикладной сервис получает состояние из хранилища.
  6. Данные преобразуются в ViewModel, предназначенную для экрана.
  7. View формирует HTML.
  8. Фреймворк возвращает HTTP-ответ браузеру.

При изменении данных поток отличается финалом. После успешного POST контроллер часто возвращает redirect на отдельный GET. Подход Post/Redirect/Get предотвращает повторную отправку формы при обновлении страницы, но не заменяет защиту от повторов на уровне бизнес-операции.

Упрощенный пример

final class OrderController
{
    public function show(int $id): Response
    {
        $order = $this->orders->getForViewer($id, $this->user);

        return $this->view('orders/show', [
            'order' => OrderViewModel::from($order),
        ]);
    }
}

Контроллер связывает запрос со сценарием, но не формирует SQL и не рисует HTML. Репозиторий или сервис получает модель, ViewModel преобразует ее для экрана, а шаблон отвечает за отображение. Названия классов будут отличаться между фреймворками, зато направление зависимостей остается понятным.

Категория средств разработки ПО содержит инструменты для разных этапов работы. IDE и генераторы кода ускоряют создание контроллеров и шаблонов, но не могут автоматически определить корректную границу бизнес-ответственности.

Model и ViewModel

Доменная модель оптимизирована под правила бизнеса, а ViewModel — под конкретное представление. Карточке заказа могут понадобиться отформатированная сумма, имя статуса, доступные действия и несколько полей клиента. Передавать в шаблон весь ORM-объект удобно, но View получает лишние данные и неявные зависимости.

ViewModel делает контракт экрана явным и не должна сохранять себя в базу. Для формы можно использовать input-модель пользовательского ввода.

Где размещать валидацию

ПроверкаПодходящее местоПример
Формат вводаRequest/input modelEmail имеет допустимый синтаксис
АвторизацияПолитика доступа или прикладной слойПользователь может менять этот заказ
Бизнес-инвариантДоменная модельЗакрытый заказ нельзя редактировать
Ограничение хранилищаБаза плюс обработка результатаЗначение должно быть уникальным
Подсказка интерфейсаViewПоказать ошибку рядом с полем

Клиентская проверка улучшает удобство, но сервер должен повторно проверить недоверенный ввод. Одно правило иногда поддерживается на нескольких уровнях: уникальный email можно предварительно проверить сервисом, однако окончательную защиту от гонки дает ограничение базы.

MVC и работа с базой

Буква M не означает, что Controller обязан напрямую вызывать ORM. В простом CRUD это допустимо, но по мере роста запросы, транзакции и бизнес-операции удобнее выделить в прикладные сервисы и репозитории. Модель не должна зависеть от того, пришел сценарий из HTTP, очереди или консольной команды.

Транзакционная граница обычно совпадает с прикладной операцией: «подтвердить заказ», а не с отдельным вызовом контроллера или шаблона. Ошибка сохраняется и преобразуется в подходящий ответ на уровне интерфейса.

MVC и трехслойная архитектура

MVC и layers отвечают на разные вопросы. MVC разделяет роли пользовательского интерфейса. Трехслойная архитектура обычно делит систему на presentation, application/business и data access. Веб-контроллер и View относятся к presentation, а Model в широком MVC-смысле может охватывать части других слоев.

ПодходФокусТипичная граница
MVCВвод, модель состояния, отображениеController, Model, View
ТрехслойныйНаправление зависимостей системыPresentation, business, data
Компонентный UIСостояние и повторное использование интерфейсаКомпоненты и их дерево

Эти подходы можно сочетать. Ошибка возникает, когда название папки принимают за архитектурную гарантию: файл в Models может содержать HTML, а маленький Controller — скрывать важную бизнес-логику в callback.

MVC в разных фреймворках

Фреймворки трактуют роли по-разному. В серверном MVC Controller принимает запрос и выбирает View; в настольном приложении он управляет объектами интерфейса; во фронтенде обязанности распределяются между компонентами и состоянием.

Поэтому спорить о «настоящем MVC» без контекста мало полезно. Сначала зафиксируйте поток управления выбранного фреймворка, затем определите границы. Официальная документация фреймворка важнее случайной схемы из другого стека.

REST API без View

API-контроллер может вернуть JSON вместо HTML. Фреймворки нередко используют тот же routing, binding, validation и filters, хотя классического View-шаблона нет. Такой код наследует принципы MVC presentation-слоя, но не обязательно реализует полную классическую триаду.

Не превращайте API Controller в место всей логики только потому, что View отсутствует. Контроллер по-прежнему валидирует форму запроса, вызывает сценарий и преобразует результат в HTTP-ответ.

Как тестировать MVC

  • доменную модель проверяйте unit-тестами без HTTP и шаблона;
  • контроллер тестируйте на выбор сценария и правильный результат;
  • роуты, binding, middleware и авторизацию проверяйте интеграционно;
  • View проверяйте snapshot- или rendering-тестами для важных состояний;
  • полный пользовательский путь оставляйте для небольшого набора end-to-end тестов;
  • отдельно проверяйте ошибки, пустые данные и запрет доступа.

Репозиторий кода и проверок может храниться в карточке вроде GitHub. Сам сервис контроля версий не обеспечивает архитектуру: ревью должно проверять направление зависимостей, размер контроллеров и наличие бизнес-логики в View.

Скоринг смешения слоев

Тест на 12 баллов

Поставьте по одному баллу за каждый признак в действии контроллера: SQL, ручное управление транзакцией, расчет цены, изменение нескольких агрегатов, отправка письма, генерация HTML, чтение глобального состояния, статические зависимости, повторяемая бизнес-ветка, более трех уровней условий, отсутствие отдельного теста сценария и метод длиннее одного экрана.

0–2 балла: действие выглядит координирующим. 3–5: найдите повторяемый сценарий и выделите сервис. 6–12: контроллер скрывает несколько ответственностей; рефакторинг стоит начать с бизнес-инвариантов и транзакционной границы. Баллы — инструмент обсуждения, а не автоматический стандарт качества.

Тот же тест применим к View: запросы к базе, изменение состояния, проверка прав и расчеты повышают риск. Цель не в минимальном числе строк, а в том, чтобы каждый компонент имел понятную причину для изменения.

Канвас одного сценария

Перед реализацией заполните короткую карту:

ПолеВопрос
ВходКакой route, метод и параметры получает интерфейс?
АкторКто выполняет действие и какое право требуется?
ИнвариантЧто должно оставаться истинным при любой точке входа?
ОперацияКак называется один прикладной сценарий?
ТранзакцияКакие изменения должны завершиться вместе?
РезультатКакая ViewModel, ошибка или redirect возвращается?
Побочный эффектКак запускаются письмо, событие или очередь?
ТестКак проверить правило без реального браузера?

Канвас помогает заметить, что действие «сохранить форму» на самом деле содержит несколько операций. Разделяйте их по бизнес-смыслу, а не по числу экранов.

Когда MVC подходит

  • приложение имеет несколько пользовательских сценариев и представлений;
  • бизнес-правила должны развиваться отдельно от верстки;
  • фреймворк поддерживает понятный request lifecycle;
  • команде нужны независимые тесты модели и интерфейса;
  • одни данные показываются в разных форматах;
  • требуется прозрачное распределение ответственности.

Для маленькой статической страницы полный набор слоев может быть лишним. Для сложного интерактивного клиента классическое серверное MVC может быть только частью архитектуры. Выбирайте паттерн по потоку изменений и компетенциям команды, а не по популярности аббревиатуры.

Как внедрять MVC в существующий код

  1. Выберите один часто меняющийся сценарий.
  2. Опишите его вход, инварианты, результат и побочные эффекты.
  3. Зафиксируйте тест текущего поведения.
  4. Вынесите бизнес-правила из Controller и View в модель или сервис.
  5. Создайте ViewModel только с данными экрана.
  6. Оставьте в Controller координацию и преобразование результата.
  7. Проверьте маршрут и отображение интеграционным тестом.
  8. Повторяйте по сценариям, не переписывая все приложение сразу.

Командная работа над изменением может проходить через карточку вроде GitLab. В merge request полезно явно указывать, где находится бизнес-правило, где формируется ViewModel и каким тестом подтверждена граница.

Типичные ошибки

  • считать любой ORM-класс полноценной доменной моделью;
  • писать SQL и бизнес-правила в Controller;
  • выполнять запросы к базе из View;
  • передавать шаблону весь объект с лишними полями;
  • дублировать валидацию без единого источника бизнес-правила;
  • создавать сервис на каждую строку без реальной ответственности;
  • связывать модель с HTTP и конкретным шаблонизатором;
  • проверять только happy path;
  • путать структуру каталогов с архитектурой;
  • копировать MVC-схему другого фреймворка без учета lifecycle.

Чек-лист границ

  • Controller читается как краткий сценарий;
  • View не меняет состояние и не обращается к базе;
  • бизнес-инварианты выполняются вне HTTP-контекста;
  • ViewModel содержит только данные представления;
  • авторизация проверяется до операции;
  • транзакция охватывает целостное действие;
  • ошибки переводятся в понятный результат;
  • зависимости передаются явно;
  • повторяемые сценарии не копируются между контроллерами;
  • у каждого слоя есть независимый тест подходящего уровня.

Частые вопросы

MVC — это архитектура всего приложения?

Не обязательно. Это паттерн организации интерфейсного взаимодействия. Крупное приложение дополнительно использует прикладные, доменные и инфраструктурные границы.

Модель равна таблице базы?

Нет. Таблица описывает хранение, а модель — состояние и поведение предметной области. В простом CRUD один ORM-класс может выполнять обе роли, но это не обязательное правило.

Можно ли Controller обращаться к репозиторию?

В простом сценарии можно. Если появляются транзакции, несколько источников и бизнес-ветки, лучше вызвать отдельный прикладной сервис, который выразит операцию целиком.

Должен ли View быть полностью без логики?

Нет. Логика отображения допустима: формат, цикл, условный блок. Бизнес-решения, изменение состояния и доступ к данным в View размещать не следует.

MVC устарел из-за SPA?

Нет, но область применения изменилась. Сервер может использовать MVC для HTML или API, а клиент — компонентную модель состояния. Важно описать границы конкретной системы.

Вывод

MVC полезен не названием каталогов, а ясным разделением причин для изменения. Model защищает правила и состояние, View представляет подготовленные данные, Controller переводит пользовательский запрос в один сценарий и выбирает ответ. Проследите путь реального запроса, измерьте смешение обязанностей, выделите ViewModel и тестируйте границы: так паттерн уменьшает связанность, а не добавляет формальные слои.

Похожие материалы

Все статьи
К Блог Как использовать ChatGPT для психологической поддержки ChatGPT может помочь подобрать слова для переживаний, разложить запутанную ситуацию на факты и предположения, подготовит... К Блог Как использовать ChatGPT для решения задач по геометрии ChatGPT может помочь перевести условие задачи по геометрии в понятную запись, предложить план доказательства и объяснить... К Блог Как анализировать внешность по фото с помощью ChatGPT ChatGPT с поддержкой изображений может описать элементы фотографии: одежду, прическу, сочетание цветов, освещение, ракур...