К 2021 году появилось много статей о том, что фреймворки не нужны и не стоит делать из них культ. Отчасти это верно. Зависимость от фреймворка затрудняет процессы рефакторинга и тестирования, часто негативно влияет на выстраивание бизнес-логики приложения. Но во всём нужен разумный подход. И прежде чем встать на путь отрицания фреймворков, руководитель Программного комитета PHP Russia Александр Макаров советует прочитать статью Маттиаса Нобака (Matthias Noback) «Should we use a framework?»
В статье Маттиас рассказывает о том, какие вопросы должен задать себе разработчик, прежде чем выбрать фреймворк или отказаться от фреймворков вообще. Перевод статьи читайте под катом. В комментариях делитесь своим опытом выбора и использования фреймворков.
![](https://habrastorage.org/r/w780q1/webt/lc/ee/ne/lceene_mz3bhmr9uvmxcbmtrpz4.jpeg)
Так как я много пишу о разработке распределённых приложений, неудивительно, что один из моих читателей задал вопрос: «Зачем использовать фреймворк?». Короткий ответ: потому что он вам нужен. И вот почему:
В общем, фреймворк нужен всем. Но вам всё равно стоит писать независимый от фреймворка код там, где это возможно. Ведь если весь код проекта связан с фреймворком, то:
Формирование крепкого ядра изолированного кода, не привязанного к технологии баз данных или конкретному фреймворку, даёт больше свободы и предотвращает все вышеперечисленные проблемы. Если вы хотите написать независимый от фреймворка код, не нужно изобретать велосипед. Можно положиться на каталог шаблонов проектирования, например:
Ни один из перечисленных классов не будет использовать такие специфичные для фреймворка вещи, как:
Для себя я установил несколько правил для проверки изолированности бизнес-логики в проекте:
Пункты 1 и 2 должны быть выполнены на все 100%. Что касается пункта 3, здесь могут быть варианты из-за извечной проблемы сопоставления сущностей с форматом их хранения. Например, у вас может быть некоторая логика сопоставления в вашей сущности (т.е. инструкции для вашего ORM о том, как сохранять сущности), но не должно быть никаких сервисных зависимостей, специфичных для выбранного вами хранилища. Например вы не можете внедрить EntityManagerInterface или использовать QueryBuilder где-либо в коде. Кроме того, вызовы методов никогда не должны приводить к реальным запросам к базе данных, даже если это Sqlite.
Если вы выполните все эти условия, ваш фреймворк будет выглядеть как обёртка вокруг изолированного ядра:
![](https://habrastorage.org/r/w1560/webt/je/ke/-e/jeke-ercyglfyhfz9wtltvqgjem.png)
Этот слой содержит всю техническую информацию. Здесь вы найдете такие аббревиатуры, как SQL, ORM, AMQP, HTTP и другие. И именно здесь не стоит писать всё с нуля. Мы используем мощь фреймворков и библиотек, чтобы не отвлекаться на решение низкоуровневых проблем, а сосредоточиться на бизнес-логике и взаимодействии с пользователем.
Фреймворк должен помочь вам:
Я считаю, что достаточно хороший фреймворк должен обладать вот такими качествами:
В идеале фреймворк также:
Обратите внимание, всё это необязательные условия! Если ограничиться технологическим слоем вокруг вашего изолированного ядра, то фреймворк, скорее всего, будет использовать разные практики, которые мне кажутся вредными. Но пока эти практики не влияют на дизайн ядра, всё в порядке. Тем не менее, поддержка такой архитектуры требует определённой дисциплины. Когда дело доходит до изолированного ядра, не стоит срезать углы!
В статье Маттиас рассказывает о том, какие вопросы должен задать себе разработчик, прежде чем выбрать фреймворк или отказаться от фреймворков вообще. Перевод статьи читайте под катом. В комментариях делитесь своим опытом выбора и использования фреймворков.
![](https://habrastorage.org/webt/lc/ee/ne/lceene_mz3bhmr9uvmxcbmtrpz4.jpeg)
Так как я много пишу о разработке распределённых приложений, неудивительно, что один из моих читателей задал вопрос: «Зачем использовать фреймворк?». Короткий ответ: потому что он вам нужен. И вот почему:
- Фреймворк делает за вас слишком многое. Вам потребуется уйма времени и денег, чтобы заменить всё это на самостоятельно написанный вами код.
- Разработчики, поддерживающие фреймворк, исправили множество проблем ещё до того, как вы с ними столкнулись. Они постоянно заботятся о безопасности кода и исправляют проблемы по мере их появления. Вам остаётся только загрузить последнюю версию фреймворка.
- Отказавшись от фреймворка, вы не будете зависеть от Symfony, Laravel, Yii и так далее. При этом вы будете зависеть от своего фреймворка, а это ещё большая проблема, так как поддерживать его вам и очень вероятно что делать это вы не будете (по моему опыту, в проектах с самописным фреймворком, поддержкой самого фреймворка почти никто не занимается).
В общем, фреймворк нужен всем. Но вам всё равно стоит писать независимый от фреймворка код там, где это возможно. Ведь если весь код проекта связан с фреймворком, то:
- Сложно угнаться за изменениями в фреймворке. При обновлении API, пользовательских соглашений или лучших практик фреймворка обновление кода проекта занимает слишком много времени;
- Трудно протестировать любую бизнес-логику без прохождения фронт-контроллера, анализа HTML-ответа или просмотра базы данных;
- Тестировать что-либо будет сложно, потому что изолированное тестирование в таких случаях невозможно. Обязательно нужно будет настроить схему базы данных, заполнить ее данными или поднять какой-либо сервисный контейнер.
Формирование крепкого ядра изолированного кода, не привязанного к технологии баз данных или конкретному фреймворку, даёт больше свободы и предотвращает все вышеперечисленные проблемы. Если вы хотите написать независимый от фреймворка код, не нужно изобретать велосипед. Можно положиться на каталог шаблонов проектирования, например:
- Сервисы приложения и командные объекты.
- Сущности и интерфейсы репозиториев.
- События домена и их подписчики.
Ни один из перечисленных классов не будет использовать такие специфичные для фреймворка вещи, как:
- Запрос, ответ, сессия, хранилище токенов или менеджер безопасности,
- Локаторы сервисов, хэлперы для конфигурации, разрешители зависимостей,
- Подключения к базе данных, построители запросов, мапперы данных (или что там используется в вашем фреймворке).
Для себя я установил несколько правил для проверки изолированности бизнес-логики в проекте:
- Могу ли я перенести это приложение из веба в консоль, не затрагивая ни один из основных классов?
- Могу ли я создать экземпляры всех классов в ядре моего приложения без подготовки какого-либо специального контекста или настройки внешних сервисов?
- Могу ли я перенести это приложение из базы данных SQL в документную базу данных, не затрагивая ни один из основных классов?
Пункты 1 и 2 должны быть выполнены на все 100%. Что касается пункта 3, здесь могут быть варианты из-за извечной проблемы сопоставления сущностей с форматом их хранения. Например, у вас может быть некоторая логика сопоставления в вашей сущности (т.е. инструкции для вашего ORM о том, как сохранять сущности), но не должно быть никаких сервисных зависимостей, специфичных для выбранного вами хранилища. Например вы не можете внедрить EntityManagerInterface или использовать QueryBuilder где-либо в коде. Кроме того, вызовы методов никогда не должны приводить к реальным запросам к базе данных, даже если это Sqlite.
Если вы выполните все эти условия, ваш фреймворк будет выглядеть как обёртка вокруг изолированного ядра:
![](https://habrastorage.org/webt/je/ke/-e/jeke-ercyglfyhfz9wtltvqgjem.png)
Этот слой содержит всю техническую информацию. Здесь вы найдете такие аббревиатуры, как SQL, ORM, AMQP, HTTP и другие. И именно здесь не стоит писать всё с нуля. Мы используем мощь фреймворков и библиотек, чтобы не отвлекаться на решение низкоуровневых проблем, а сосредоточиться на бизнес-логике и взаимодействии с пользователем.
Фреймворк должен помочь вам:
- Обеспечить плавный переход выполнения от входящего HTTP-запроса к вызову одного из контроллеров.
- Загрузить, распарсить и проверить конфигурацию приложения.
- Инстанциировать любой сервис, необходимый для выполнения работы.
- Преобразовать ваши данные в сообщения в очереди, которые далее могут обрабатываться внешними воркерами.
- Проанализировать аргументы командной строки и передать их как готовые к работе примитивные типы.
- Превратить данные вашего приложения в записи базы данных и обратно в данные, которые вы можете использовать в своем приложении.
- Отправить HTTP-запросы к внешним сервисам и обработать ошибки соединения и статус ошибок.
- И так далее...
Я считаю, что достаточно хороший фреймворк должен обладать вот такими качествами:
- Хорошо справляется с задачами, обозначенными выше, чтобы мне не нужно было заменять или расширять что-либо самописными реализациями.
- Помогает разработчику: не нужно читать код, чтобы узнать, почему что-либо не работает.
- Экономит время на размышления обо всём этом, предоставляя правильные абстракции.
- Сопровождается хорошей, полной и актуальной документацией, так что каждый может узнать, как с ним работать
- Имеет четкие инструкции по обновлению, которые упрощают переход между версиями (или же это делается автоматически).
В идеале фреймворк также:
- Проводит четкое различие между сервисами и объектами других типов.
- Предоставляет неизменяемые объекты и сервисы без состояния, которые можно вызывать любое количество раз.
- Не предоставляет глобально доступные функции, позволяющие получать сервисы или конфигурацию.
- Запрещает изменять своё поведение через расширение классов. Всё-таки наследование — это опасная форма связанности.
Обратите внимание, всё это необязательные условия! Если ограничиться технологическим слоем вокруг вашего изолированного ядра, то фреймворк, скорее всего, будет использовать разные практики, которые мне кажутся вредными. Но пока эти практики не влияют на дизайн ядра, всё в порядке. Тем не менее, поддержка такой архитектуры требует определённой дисциплины. Когда дело доходит до изолированного ядра, не стоит срезать углы!
28 июня мы соберёмся на PHP Russia 2021. Обсудим, в том числе, фреймворки и библиотеки, расскажем об опыте реализации крупных проектов на PHP. У вас будет возможность пообщаться с core-разработчиками языка. Билеты уже в продаже. Присоединяйтесь!