- PVSM.RU - https://www.pvsm.ru -
В статье будут освещены следующие проблемы разработки и поддержки проектов на базе php-фреймворка Yii:
Yii я использовал в четырёх коммерческих проектах разной степени сложности. Это были, следующие решения:
Помимо этого я экспериментировал с этим фреймворком, обеих версий, в некоторых своих pet-проектах. В этой статье, я бы хотел осветить проблемы разработки и поддержки проектов разной степени сложности на Yii и дать ряд рекомендаций, основанных на собственном опыте.
Последний из перечисленных выше проектов (№4), разрабатывался около двух лет, к началу моего участия в нём. Ещё на собеседовании, когда я узнал о двухлетней разработке, без релиза, об использовании первой версии фреймворка (в начале 2015 года), я подумал что без проблем там не обойдётся. Но я согласился участвовать, чтобы принять challenge — взяться за то, что принято называть «унаследованный код». Да, тот самый, что вызывает самые разнообразные отрицательные чувства — отвращение, ненависть, отчаяние, страх — и следуя советам Фаулера, вооружившись тестами, сделать из долгостроя качественный, поддерживаемый, рабочий продукт. Год рефакторинга, смена архитектуры за несколько месяцев перед релизом и сам выход в продакшен проекта с подобной историей, заслуживают отдельной статьи — я получил бесценный опыт, которым хотел бы поделиться, если на то будет поля сообщества.
В этом материале я сделаю акцент на практиках способствующих созданию поддерживаемых приложений. Приведенные примеры я применял с Yii 1 и 2, но концептуально все шаблоны проектирования имеют право на жизнь в любом другом фреймворке или технологии.
Yii хорош низким порогом вхождения и тем, что способствует быстрому прототипированию. Эти же качества и являются источниками проблем.
Низкий порог входа способствует привлечению разработчиков с низкой квалификацией, которые склонны быстро увеличивать технический долг. В долгосрочной перспективе, для серьёзного проекта, это существенный риск. Для менеджмента же это, на начальном этапе, соблазн сэкономить бюджет и время при подборе команды.
Быстрое прототипирование, может создавать у заказчика и менеджмента иллюзию, что проект уже почти готов и можно начинать его эксплуатацию, а добавление функционала в будущем будет обходиться так же дёшево, как и на этапе прототипа. Такое развитие событий крайне невыгодно для разработчиков и чревато неоценимыми рисками для менеджмента и проекта в целом. Я склонен считать, что прототип должен выкидываться — это есть суть прототипа.
Модульное тестирование в сообществе Yii-разработчиков не распространено повсеместно. Есть обзор тестирования в официальной документации (1, 2), многие из расширений создаваемых энтузиастами покрыты тестами. Но, оттого что в сообществе большой процент разработчиков с малым опытом, которые ещё не доросли до автоматического тестирования, начинать проект на Yii по TDD в целом, скорее не принято.
Дополнительную сложность для юнит-тестирования может представлять использование шаблона Active Record. Об этом я подробнее напишу ниже.
В упомянутом проекте проводилось только ручное приёмочное тестирование. Были не поддерживаемые тесты под Selenium, почему-то написанные на Java, но они выполнялись слишком долго, а запускались слишком сложно, и, как следствие редко, результат давали неоднозначный.
Одно из первых мероприятий, предпринятых мной было подключение PHPUnit и применение TDD при разработке новых фичей и рефекторинге существующего кода, обучение других членов команды методикам автоматического тестирования.
Позже был использован Codeception для приёмочного тестирования API. Позже был использован Codeception для приёмочного тестирования API. Были попытки использовать его и для функционального тестирования веб-интерфейса, но на фронтенде, начавшем к тому моменту активно использовать AngularJS были проблемы с поддержкой таких тестов, пришлось временно отказаться.
О себе я могу сказать — test infected. Чего и Вам желаю. Я не могу разрабатывать без тестов. Я не верю в работоспособность чьего-либо кода, если на него нет тестов. Я верю что любой код, на который нет авто-тестов будет отправлен на помойку. Он просто будет ломаться со временем и в какой-то момент окажется, что дешевле его переписать с нуля и с тестами, чем приводить в работоспособное состояние, путём отладки вручную.
В Yii есть механизм для работы с фикстурами, который во многом компенсирует сложности тестирования Active Record. Поддержка Ваших фикстур — то о чём стоит задуматься с самого начала. Используйте для их организации способы, которые облегчат будущую поддержку:
Active Record — это тоже один из плюсов и одновременно минусов фреймворка. Плюс в том, что при прототипировании database first подход, поощряемый кодогенерацией классов моделей по схеме, может существенно ускорить разработку, а также в том, что порой проще работать с моделью, в контексте которой можно делать и запросы и валидацию и вообще всё что с ней связано. Минусы начинаются, когда бизнес-логика слишком велика для того чтобы всю её помещать в модель, и нарушение принципа единственной ответственности даёт о себе знать — Active Record это же и Domain Model, и Repository, и Data Mapper, и Table и Row Gateways одновременно.
Если Вы видите, что предметная область сложнее сайта-визитки или банального блога — используйте альтернативные шаблоны проектирования, чтобы разгрузить ваши модели: в Yii есть разнообразные пути для этого:
Такой подход проще в тестировании и позволяет моделям не заплыть «жиром».
Вообще предпочитайте композицию наследованию — старайтесь выносить функциональность из иерархии наследования в делегаты с узко-специализированным интерфейсом.
«Жирные» модели, один из самых распространенных антипаттернов, которые я видел в проектах на Yii. Я предпочитаю следующие подходы при работе с ActiveRecord:
По сути, экземпляр приложения, статически доступный как Yii::app() — есть сам себе singleton и сервис-локатор для компонентов приложения. Если без ООП фанатизма, это вполне рабочее решение, если вам не нужно иметь два разных экземпляра приложения в одном процессе.
Не скупитесь создавать сервисы — маленькие сервисы с минимальной ответственностью хорошо в тестировании, они предсказуемы и понятны. Для решения любой бизнес задачи удобно создать специализированный сервис, которые будет решать ровно одну эту задачу. Я предпочитаю в качестве компонентов приложения регистрировать фабрики таких сервисов — это позволяет наделять сервисы состоянием при необходимости. Так же фабрика даёт дополнительную гибкость при инициализации объектов, в сравнении с наиболее распространённым, судя по примерам в сети, подходом напрямую регистрировать сервисы с предопределённым состоянием в конфигурации приложения.
Организованные таким образом сервисы с узкой специализацией легко заменять моками, чтобы тестировать бизнес-логику более высокого уровня. Ну, а соблюдая принцип единой ответственности на уровне каждого из сервисов, не составляет труда протестировать каждый из них в отдельности.
Для автокомплита ваших компонентов воспользуйтесь рецептом от Александра Макарова [7].
Так как первая версия Yii была выпущена в конце 2008 года и поддерживает php 5.1 — в ней не используются пространства имён на уровне ядра. Но это не мешает Вам использовать их. В проектах на первой версии я успешно использовал их.
Вместо использования т. н. Aliases Вы можете использовать статические имена классов — загрузчик их подхватит. То есть, конфигурация может выглядеть следующим образом (на примере модуля из официальной документации):
// «старый» подход
'modules'=>array(
'testmodule' => array(
'class' => 'application.modules.testmodule.TestModuleModule',
),
),
// рекомендуемый в документации подход, для версий PHP >= 5.3
'modules'=>array(
'testmodule' => array(
'class' => 'mynamespacemodulestestmoduleTestModuleModule',
),
),
// в 5.5 и выше это можно делать ещё удобнее
'modules' => [
'testmodule' => [
'class' => mynamespacemodulestestmoduleTestModuleModule::class,
],
],
И ещё пара улучшение для Вашего кода, о которых Вы возможно не знали, или забыли:
Одним из недостающих классов в Yii1 для меня был webResponse, при том что существует Request. Написание простейшей реализации класса ответа, и добавление супер-класса для контроллеров, с обработкой оного в afterAction(), позволило модульно тестировать контроллеры, и в итоге избавить из от лишнего «жира».
Вообще хорошим подспорьем будет введение наследников и супер-классов для каждого из используемых компонентов фреймворка: контроллеров, моделей, поведений и т. п.
А ещё, у сообщества есть полезные расширения, не только функциональные, но архитектурные. К примеру мы в проекте используем ObjectWatcher — реализация Identity Map — для того чтобы в разных контекстах работать иметь одни и те же экземпляры моделей, и NestedTransaction.
Не забывайте мудрые слова Стива МакКоннелла:
Программируйте с использованием языка, а не на языке.
Эта сентенция справедлива и для фреймворков: будьте Разработчиками, а не %framework_name%-программистами!
Автор: samizdam
Источник [8]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/php-2/119068
Ссылки в тексте:
[1] Главные достоинства и недостатки: #double-edged
[2] Тестирование: #testing
[3] Нюансы использования ActiveRecord: #active-record
[4] Сервис-ориентированный подход: #services
[5] Новшества языка: #namespaces
[6] Расширение фреймворка: #extends
[7] рецептом от Александра Макарова: http://rmcreative.ru/blog/post/yii--avtokomplit-dlja-yiiapp
[8] Источник: https://habrahabr.ru/post/282407/
Нажмите здесь для печати.