Архитектура приложений и фреймворки (MVC, Laravel/Symfony)
Связь с предыдущими темами курса
Ранее мы уже собрали три ключевых слоя веб-разработки на PHP:
язык и экосистема (Composer, PSR, автозагрузка)
HTTP-взаимодействие (запрос, ответ, формы, сессии, файлы)
доступ к данным (SQL, PDO, репозитории, основы ORM)Следующий логичный шаг — понять, как организовать код, чтобы приложение оставалось расширяемым и поддерживаемым. Для этого нужны архитектурные принципы и, в большинстве реальных проектов, фреймворк.
Цель этой статьи: связать воедино HTTP-часть и работу с базой данных через понятную архитектуру и показать, как Laravel и Symfony реализуют эти идеи на практике.
Зачем нужна архитектура веб-приложения
Пока проект маленький, можно писать обработчики прямо в public/index.php. Но с ростом требований появляются типичные проблемы:
трудно найти, где именно реализована логика
повторяется код валидации, формирования ответов, работы с БД
становится страшно что-то менять, потому что всё связано со всем
тестирование почти невозможно без запуска всего приложенияАрхитектура — это набор решений о структуре кода, границах ответственности и способах связи компонентов. В веб-приложениях ключевая идея простая: разделить код на части так, чтобы изменения в одном слое минимально затрагивали остальные.
MVC как базовый архитектурный шаблон
MVC (Model–View–Controller) — классический шаблон для веб-приложений. Он отвечает на вопрос: кто за что отвечает при обработке запроса.
!Диаграмма показывает роли Model, View и Controller и поток данных между ними
Controller (контроллер)
Контроллер — это слой, который связывает HTTP и бизнес-логику.
Обычно он:
читает входные данные запроса
запускает валидацию (на практике часто через отдельные классы)
вызывает сервисы/модели
формирует HTTP-ответ (HTML, JSON, redirect)Важно: контроллер не должен содержать сложную бизнес-логику и тем более “тяжёлые” SQL-запросы.
Model (модель)
Слово “модель” в MVC трактуют по-разному. В профессиональной PHP-практике полезно мыслить так:
доменная модель — правила и сущности предметной области (например, Пользователь, Заказ)
доступ к данным — репозитории/ORM, которые читают и пишут данные
сервисы — сценарии использования (use cases), которые координируют операцииТо есть “Model” — это не обязательно “один класс на таблицу”, а весь слой, где живёт логика приложения.
View (представление)
View отвечает за отображение данных:
HTML-шаблоны (страницы)
формирование JSON (в API это часто делает контроллер или отдельный responder)В Symfony типичный шаблонизатор — Twig, его документация: Документация Twig
В Laravel используется Blade, документация: Документация Blade
Где MVC полезен, а где его недостаточно
MVC хорошо объясняет “скелет” веб-приложения, но в реальных проектах его обычно расширяют:
добавляют слой Service (сценарии) между Controller и Model
добавляют слой Repository между логикой и БД
вводят DTO (объекты переноса данных), чтобы не таскать ассоциативные массивыПрактическая мысль: MVC — удобная отправная точка, но качество архитектуры определяется тем, как вы держите границы ответственности внутри “Model”.
Слоистая архитектура: как связать HTTP и БД без хаоса
Чтобы устойчиво расти, приложению полезно разделение на слои. Один из практичных вариантов выглядит так:
!Схема показывает слои приложения и направление зависимостей
Presentation слой
Это всё, что касается доставки данных пользователю и получения данных от него:
HTTP-контроллеры
роутинг
middleware (прослойки запроса)
сериализация в JSON
шаблоныApplication слой
Это сценарии использования (use cases): “зарегистрировать пользователя”, “создать пост”, “оплатить заказ”.
Обычно этот слой:
принимает уже нормализованные данные
работает через интерфейсы репозиториев и сервисов
управляет транзакциями (либо делегирует это инфраструктуре)Domain слой
Здесь живут правила предметной области:
сущности и value objects
проверки инвариантов (например, “нельзя оплатить отменённый заказ”)
доменные события (если вы до них дойдёте)Идеал: домен минимально зависит от фреймворка.
Infrastructure слой
Это конкретные реализации:
PDO/Doctrine/Eloquent
клиенты внешних API
очередь, кэш
файловая системаГлавная идея: инфраструктуру легче менять, если она спрятана за интерфейсами и не “протекает” в бизнес-логику.
Dependency Injection и контейнер: как компоненты связываются между собой
Dependency Injection (DI) — это способ передавать зависимости объекту снаружи, а не создавать их внутри.
Плохой признак:
класс сам создаёт new PDO(...), сам читает .env, сам решает, какой логгер использоватьХороший признак:
класс получает зависимости через конструктор и не знает, как они устроеныDI-контейнер
DI-контейнер — компонент, который:
создаёт объекты
понимает, какие зависимости нужны
умеет подставлять нужные реализации (например, “в проде один логгер, в тестах другой”)Symfony активно использует контейнер как основу приложения: Документация Symfony Service Container
Laravel тоже использует контейнер: Документация Laravel Service Container
Жизненный цикл HTTP-запроса во фреймворках
На базовом уровне фреймворки делают то, что мы уже писали вручную во фронт-контроллере, но системно и расширяемо.
!Схема показывает этапы обработки HTTP-запроса от входа до ответа
Типовой конвейер:
веб-сервер направляет запрос в public/index.php
фреймворк создаёт объект запроса
роутер сопоставляет путь и метод с обработчиком
middleware выполняют общие действия (аутентификация, защита, логирование)
контроллер вызывает нужный сценарий
формируется объект ответа
ответ отправляется клиентуMiddleware
Middleware — это “прослойка” вокруг обработки запроса. Каждое middleware может:
остановить обработку и вернуть ответ сразу
изменить запрос
изменить ответ после выполнения контроллераSymfony опирается на компонент HTTP Foundation: Документация Symfony HttpFoundation
В более стандартизированном мире PHP часто упоминают PSR-7 и PSR-15:
PSR-7: HTTP message interfaces
PSR-15: HTTP server request handlersНе все фреймворки “чисто” следуют PSR-7 внутри, но эти стандарты полезны для понимания экосистемы.
Роутинг и контроллеры: превращаем URL в код
Роутинг — это таблица соответствий “метод + путь” → “обработчик”.
В Symfony роуты можно задавать по-разному, один из популярных способов — атрибуты: Документация Symfony Routing
В Laravel — через файл маршрутов: Документация Laravel Routing
Профессиональная практика для контроллеров:
контроллеры тонкие
валидация вынесена (Form Request в Laravel или Validator-компонент в Symfony)
бизнес-логика в сервисах/сценарияхРабота с данными во фреймворках: ORM, репозитории, миграции
Вы уже знаете PDO и prepared statements. Во фреймворках чаще используют ORM, но принципы безопасности и контроля данных не исчезают.
Symfony и Doctrine
В Symfony-дефолтной экосистеме часто используют Doctrine:
ORM: Doctrine ORM
миграции: Doctrine MigrationsОсобенности Doctrine (важные для архитектуры):
Unit of Work: ORM копит изменения и применяет их при flush()
lazy loading: связи могут догружаться “по требованию”, что может привести к N+1Laravel и Eloquent
Laravel обычно использует Eloquent ORM: Документация Laravel Eloquent
Особенности Eloquent:
активная запись (Active Record): модель часто знает, как себя сохранить
быстрый старт для CRUD
важно следить за выборками и eager loading, чтобы не получить N+1N+1 запрос как типичная ловушка ORM
Проблема N+1 возникает, когда вы:
получили список сущностей одним запросом
затем для каждой сущности отдельно догрузили связанный объектИтого получается запросов вместо 2–3. Решение обычно в “жадной загрузке” (eager loading) или корректном запросе.
Конфигурация, окружение и секреты
В профессиональном проекте конфигурация отделена от кода:
разные значения для dev/stage/prod
секреты не лежат в репозиторииLaravel часто использует .env и helper-функции для доступа к конфигурации: Документация Laravel Configuration
Symfony использует переменные окружения и пакет Dotenv: Документация Symfony Dotenv
Практическое правило:
храните секреты в окружении или менеджере секретов
не логируйте секреты
не возвращайте пользователю детали ошибокОбработка ошибок и логирование
Фреймворки дают инфраструктуру, которую вручную обычно пишут позже:
централизованный обработчик исключений
страницы/ответы ошибок для разных окружений
логированиеПопулярный логгер в PHP — Monolog: Monolog
Важный архитектурный принцип:
исключения и логи должны помогать разработчику
пользователь должен получать безопасное и понятное сообщениеСтруктура проекта: как выглядит “взрослый” код
Ниже — пример фреймворк-агностичной структуры, которая хорошо маппится и на Symfony, и на Laravel:
Смысл в том, чтобы:
HTTP-слой не знал деталей базы
домен не зависел от фреймворка
инфраструктура была заменяемойLaravel и Symfony: как выбрать
И Laravel, и Symfony — зрелые фреймворки. Разница чаще в подходе и “ощущениях” от разработки, чем в возможности решить задачу.
| Критерий | Laravel | Symfony |
|---|---|---|
| Входной порог | Обычно ниже, быстрый старт | Выше, но более “конструктор” |
| Стиль | Много готового “из коробки” | Набор компонентов, строгая конфигурация |
| ORM по умолчанию | Eloquent | Doctrine (часто) |
| Типичные проекты | Продукты, CRUD, быстрые итерации | Сложные системы, долгоживущие проекты, enterprise |
| Ключевая сильная сторона | Скорость разработки и экосистема | Архитектурная дисциплина и компонентность |
Полезные отправные точки:
Документация Laravel
Документация SymfonyПрактический выбор:
если важно быстро собрать продукт и много типового CRUD, Laravel часто удобнее
если вы строите сложную систему с большим количеством модулей и долгим сроком жизни, Symfony часто проще держать в порядкеКак “перейти” от учебного кода к фреймворку
Если у вас уже есть учебное приложение с public/index.php, PDO и простыми обработчиками, переход можно сделать по шагам:
Зафиксировать структуру проекта и автозагрузку PSR-4 в Composer.
Вынести работу с БД в репозитории и запретить SQL в HTTP-слое.
Вынести бизнес-логику в сервисы (use cases).
Добавить централизованную обработку ошибок и логирование.
Подключить роутер, контейнер, шаблоны.
После этого перейти на Laravel или Symfony, перенеся слои “снаружи внутрь”.Главная мысль: фреймворк эффективен, когда вы понимаете архитектуру и умеете держать границы ответственности.
Итоги
В этой статье мы связали предыдущие темы курса в целостную картину:
HTTP-обработчики должны быть тонкими и предсказуемыми
доступ к данным должен быть вынесен и безопасен (prepared statements, репозитории, транзакции)
MVC помогает понять базовые роли, но реальная поддерживаемость достигается слоистой архитектурой
DI-контейнер и middleware делают систему расширяемой без копипаста
Laravel и Symfony по-разному упаковывают одни и те же принципы, и выбор зависит от контекстаДальше по курсу логично углубляться в практики, которые делают архитектуру “боевой”: безопасность (CSRF, XSS), тестирование, очереди/кэш, проектирование доменной модели и качество кода.