Найти тему
notnull.media

Как создавать легко поддерживаемые в будущем приложения на PHP

Оглавление

Мучались ли вы с приложением, потому что его было трудно модифицировать?

Хотели ли обновить версию фреймворка или PHP, однако для этого требовалось слишком много изменений?

Или, может быть, вы начинаете новый проект и хотите избежать подобных ситуаций?

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

* В этой статье я использую пакет как синоним библиотеки или фреймворка.

Подводные камни фреймворков и библиотек

Связь с фреймворком

Когда мы начинаем использовать классы, связанные с фреймворком, мы становимся зависимыми от него.

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

Ещё больше проблем появляется, когда мы объединяем такие классы с внешними библиотеками или модулями фреймворка. Их обновление тоже может потребовать от нас больших изменений в нашем коде. А если библиотеки или модули не соответствуют нашей новейшей версии фреймворка, нам нужно будет заменить их или оставить старую версию фреймворка.

Перегружен конфигурацией

Конфигурация, которая нацелена на имена классов, методов, пространств имен или путей, блокирует нас от свободного перемещения классов и методов и увеличивает время рефакторинга.

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

Чужая абстракция

Код, специфичный для вашего бизнеса и решающий его проблемы, является кодом, связанным с бизнесом.

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

Решение извне также добавляет дополнительные пакеты, которые нам понадобятся при следующих обновлениях.

Бизнес развивается и меняется, как и системные требования. Если мы завязываем наш код, связанный с бизнесом, на внешнем пакете, мы становимся зависимы от автором пакета.

Нужно ли нам использовать внешние пакеты для разработки?

Давайте рассмотрим ситуацию, когда мы вообще не используем никакого фреймворка. Сначала нам нужно создать некоторый объект Request, который будет обрабатывать наш входящий HTTP-запрос. Затем нам нужно будет построить систему маршрутизации, которая будет сопоставлять наш запрос с конкретным действием. Как насчёт внедрения зависимостей, нам тоже нужно связать наши классы вместе, верно?

Вы, наверное, слышали страшные истории разработчиков, которые отказались от фреймворка, а в итоге построили собственный фреймворк.

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

Мы не можем избежать внешних пакетов. Вопрос в том, как их использовать, чтобы получить пользу и избежать ловушек?

Как использовать фреймворки, библиотеки и пакеты, избегая подводных камней?

Итак, как мы можем использовать фреймворки и библиотеки таким образом, чтобы это могло помочь нам в долгосрочной поддержке приложения и возможности обновления?

Изолируйте бизнес-ориентированный код

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

Это означает реализацию классов без внешних пакетов, чтобы мы могли расширять и развивать систему на наших условиях.

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

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

Разрешить фреймворку связывать ваш код

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

При использовании веб-фреймворка, такого как Symfony и Laravel, мы будем использовать контроллеры и маршрутизацию, поэтому нам не нужно тратить время на создание и подключение HTTP-запроса к определенному классу и действию.

Другой пример — обработчики сообщений. Обработчик сообщений отвечает за обработку конкретного сообщения (класса). Благодаря этому мы можем создать несвязанное решение, в котором верхние уровни, такие как HTTP-контроллер или команда консольной строки, могут создать сообщение и отправить его, не ссылаясь на конкретный код, связанный с бизнесом. Связующий код, отвечающий за перемещение сообщения в конкретный обработчик сообщений, будет обрабатываться платформой.

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

Используйте простую конфигурацию

Предположим, мы используем фреймворк для наших обработчиков сообщений.

И мы хотим зарегистрировать наш класс:

-2

Мы хотим зарегистрировать класс OrderHandler и дескриптор метода в качестве нашего обработчика сообщений, который будет обрабатывать класс PlaceOrderMessage.

Итак, какие варианты у нас есть для регистрации этого обработчика?

1. XML или YAML

Первым решением будет использование XML или YAML. Давайте проверим, как может выглядеть такая конфигурация:

-3

Мы могли бы добиться хорошего разделения, отделив бизнес-код от связующего кода.

Однако у него есть недостаток: в случае любого рефакторинга нам нужно будет отредактировать эти файлы, и это может занять очень много времени.

Если вы знакомы с настройкой Dependency Injection через файлы конфигурации, я уверен, вам знакома эта проблема.

2. Реализация специфического интерфейса фреймворка

Вторым решением было бы реализовать интерфейс, предоставляемый фреймворком.

-4

Легко реализовать, так как нам просто нужно реализовать интерфейс или расширить определённый класс. И также легко увидеть, что этот класс является обработчиком сообщений.

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

3. Конфигурация PHP

Третьим решением было бы предоставить конфигурацию с использованием средств PHP.

-5

Это позволяет нам автоматически использовать инструменты рефакторинга для имени класса.

Мы также сохранили бы разделение, отделив бизнес-код от связующего кода.

Однако в случае модификации имя метода всё равно нужно будет изменить вручную.

И изнутри класса мы не можем ясно видеть, что он был зарегистрирован как обработчик сообщений.

4. PHP-атрибуты

Последним решением будет предоставление конфигурации с использованием атрибутов. Фреймворк найдёт все классы и методы, аннотированные #[MessageHandler]. Это позволяет нам изменить метод и имя класса, а конфигурация останется нетронутой.

Мы также предоставляем точную информацию внутри класса, что он зарегистрирован как обработчик сообщений.

У нас может быть несколько обработчиков сообщений в одном классе.

И поскольку атрибуты — это просто комментарии к нашему коду (как DocBlocks), он сохраняет разделение бизнес-кода и кода фреймворка.

Использование атрибутов PHP сделает ваш код самоописываемым и лёгким для рефакторинга и изменения.

Итог

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

Это позволит вам развивать проект на ваших собственных условиях и уменьшит количество используемых библиотек, чтобы вы могли легче обновлять своё приложение.

Используйте фреймворк, чтобы отказаться от повторяющихся задач и сосредоточиться на бизнес-коде.

Перевод Dariusz Gafka

Наш telegram: @notnullmedia