20 Июля в Киевском офисе Magento прошло открытое мероприятие под названием Magento meetup «Dare to Share», которое могли посетить и в котором могли принять участие любые желающие кому интересна платформа Magento 2 и тема электронной коммерции (eCommerce).
В данном отчете публикуются видео докладов прозвучавших на мероприятии, а также принимаются заявки на выступление в следующем ивенте.
- Игорь Миняйло, Magento (@iminyaylo on ) — Magento Community Engineering
- Макс Пронько, The Irish Store (@max_pronko on ) — Автоматизация релизов для Magento 2. Опыт компании The Irish Store
- Вячеслав Кравчук, Atwix (@slkra on ) — Story of a Transformation (Укр.)
- Андрей Кравец, Forbytes / Gymgrossisten (@Winfle on ) — Dynamic caching of personalised data in Real Life (Укр.)
- Александр Козырь, Magecom (@kozyr1av on ) — SOLID-ное программирование на Magento 2
Тезисы докладов можно прочитать под Катом
Тезисы Докладов
Magento Community Engineering
Magento Open Source — не просто ребрендинг CE, а новый подход в работе с сообществом.
Как Magento помогает сообществу, и чем она может помочь Вам. Как Вы можете помочь Magento, и что вам за это будет. Первые шаги. С чего начать.
Автоматизация релизов для Magento 2. Опыт компании The Irish Store
Реализация техники Blue-Green deployment для проектов на платформе Magento 2, чтобы уменьшить риски даунтайма и сократить время релиза с 6 часов до 15 минут.
Story of a Transformation
Доклад посвящен изменениям бизнес процессов в IT компании. О том как происходит трансформация от одной бизнес модели к другой. Как это влияет на саму компанию и на людей в ней. Социальная составляющая изменений и потребности меняться. История компании Atwix, которая работала централизовано и перешла к модели распределенной разработки, и как это на нее повлияло.
Dynamic caching of personalized data in Real Life
На сколько важна персоналиация eCommerce. Кеширование с помощью динамических блоков с помощью ESI (Varnish и Akamai), которые вычисляются для каждого отдельного клиента системой поиска и рекомендаций.
SOLID-ное программирование на Magento 2
Как следование принципам SOLID помогает упростить разработку на примере кода Magento 2.
Примеры хороших практик, а также нарушений принципов. SOLID первый шаг на пути к правильной архитектуре модульной и расширяемой системы.
Заявки на участие
Если вы хотите выступить на будущих ивентах посвященных Magento или eCommerce, пообщаться с представителями Magento сообщества, обсудить ваши проблемы и истории успеха, поделиться опытом и задать вопросы core-разработчикам — отправляйте заявки на имейл Community Engineering <uamagentoevents@magento.com>
Поделиться с друзьями
CodeKeeper
Про прмер принципа Лисков https://youtu.be/FquSm_LOmS4?t=1041
Чисто ради интереса, понимает ли докладчик, что показывает прмер использования Symfony Console Component и почему он считает что именно это пример принципа подстановки Барбары Лисков?
Про unit тестирование https://youtu.be/FquSm_LOmS4?t=1304
Никто не запрещал в M1 писать юнит тесты(вот на вскидку пример подключения ), другое дело что в большинстве случаев заказчик не оплачивал тесты и их не писали. Да, DI, SOLID помогает в написание тестов, но это не одначает что из-за этого в M1 их небыло.
Про нарушение принципов SOLID https://youtu.be/FquSm_LOmS4?t=1344
Мне интересно где это вычитал докладчик? В какой конкретно статье или литературе? Или он только ссылается на Mаgento? Да, использовать в construct в качестве тайп хинта конкретный класс вместо интерфейса не привествуется, но и не описывается как вопиющее нарушение принципа. А если нам в конкретной реализации класса нужно будет внедрить несколько хелперов и сторонний класс которые не имеют интерфейсов?
maghamed
не буду отвечать пока за автора на вопросы адресованные ему.
но эти тесты сложно было назвать Unit тестами.Отвечу на замечание по поводу Unit тестов в Magento 1.
Unit тестирование, оно же модульное предполагает выделение Unit-а, который является наименьшей тестируемой единицей, и покрытие этого модуля тестами в изоляции от работы всей системы. В классическом представлении роль Unit-а выполняет класс.
в Magento 1 вам
Потому что M1 активно использовала шаблон Service Locator для создания сущностей внутри системы. Сущности создавались по мере выполнения логики класса, этим достигался эффект Lazy Loading внешних зависимостей. В коде это было реализовано с помощью God-класса Mage и его статических методов getModel, getSingleton, getHelper. Из-за отсутствия Dependency Injection, а также из-за использования статических методов для инстанциации внешних зависимостей — разработчики были лишены возможности создать моки (Mock) или стабы (Stub) для внешних зависимостей, чтобы покрыть тестами класс в изоляции от внешних зависимостей.
Поэтому тесты, которые писались под М1 были либо функциональными либо интеграционными — тестирующие код в интеграции с системой и не использующие моки для внешних зависимостей.
maghamed
Обратите еще внимание, что класс Mage был объявлен как final
https://github.com/engineyard/magento-ce-1.9/blob/master/app/Mage.php
Теоретически, вы могли написать свой фреймворк, где бы вы создали тестовое окружение (environment), в котором по-другому инициализировали бы Mage object
https://github.com/engineyard/magento-ce-1.9/blame/master/app/Mage.php#L606
через _setConfigModel($options = array())
Но опять таки — это не Unit тестирование в классическом понимании. Более того, это настолько не просто и не очевидно. Что так никто не делал.
CodeKeeper
Вопрос не в том сложно их было назвать unit или нет. Вопрос в том, что докладчик утверждает что unit тесты отстутствуют в M1(наверное потому что не шли с под коробки), а это как минимум не совсем корректно. Опять же наскидку про unit tests упоминается на Inchoo и на Atwix
Ну если сильно извратится то можно мокнуть и финальный класс , но изврат еще тот, так что согласен.
maghamed
ну если вы Unit-ом назовете всю систему, то да, это Unit-тесты :)
Как я говорил выше, тестирование достигалось путем определенной инициализации Mage::app класса.
Где определенные базовые сущности, например Layout, Session объекты подменялись на предопределенные моки, т.е. Всю систему конфигурировали так, что она запускалась в тестовом режиме.
Собственно две статьи выше демонстрируют именно эту технику.
Почувствуйте разницу — протестировать объект в изоляции от всей системы vs сконфигурировать систему, что она запускается в режиме для тестирования.
Кстати, код теста из статьи
четко показывает, что здесь мокается только зависимость, которая передается через сеттерный метод.
Очень непрозрачно отслеживать и «мокать» зависимости, которые приходят не через сетеры, а создаются на ходу через Lazy Loading внутри методов, так как в этих подходах нет механизма фикстур и нет возможности подменить дополнительные сущности на моки для определенного теста.
CodeKeeper
Тут согласен. Но тогда стоит отметить, что и в M2 не все так прозрачно, за счет использования Magento\Framework\TestFramework\Unit\Helper\ObjectManager
maghamed
прелесть в том, что вы Можете его не использовать, если вам он не нравится, или вам кажется это магией. В M2 Unit тесты могут писаться и без его использования. Кстати, таких достаточно много, так как у нас внутри нет требования его использовать, и многие программисты считают, что лучше создавать все «вручную».
Его задача — избавить вас от надобности нагенеривать кучу моков вручную. Опять же к разговору как Magento борется с шаблонным кодом (boilerplate code).
CodeKeeper
Пользуясь случаем. раз мне ответил сам Magento 2 Architect(если верить описанию), хочу задать пару вопросов.
Почему в magento2 была выбрана стратегия кодогенерации?
Почему в magento2 не сделали явное описание создания сервисов, как например в symfony? Зачем нужна эта магия?
maghamed
Написанному можно верить :)
Вы про код генерацию в принципе или про что-то конкретное Interceptor, Factory, Proxy?Мы используем код генерацию там, где считаем она избавит программиста от написания шаблонного кода (boilerplate code).
Например, логика Factory и Proxy в шаблонном случае одинакова — поэтому мы кодгенерим классы просто когда программист указывает нужный суфикс (Factory или Proxy) добавляя его к имени сущности, которую он хочет создать.
в случае интерсептеров мы также избавляем программиста от написания шаблонного кода. А меньше кода — меньше возможностей допустить ошибку.
Я так понимаю, вопрос продиктован некоторой сложностью дебага кода с интерсептерами. Но уже существуют плагины к PHP Storm, которые решают эту проблему и убирают из стека служебные классы, для удобста чтения.
В Magento 2 используется концепт Service Layer, и API находятся в папках каждого модуля. На эти API мапятся Web API. Например, здесь можно почитать как API добавлялись для одного из модулей.
Я не понял какая именно магия имеется в виду.
CodeKeeper
Да именно про это. Ок, допустим. Но теперь времяна разработку увеличилось, за счет того что при добавлении новой зависимости в класс, например кастомной CollectionFactory(и кстати это не очевидно, поскольку на момент добавления зависимости класс еще не сгенерирован), нужно заново перегенирировать код. У меня на ноуте(i3, 8GB Ram, 512 SSD) на перегенерацию кода на чистой мадженте с одним катсомным модулем уходит 4 минуты! И при том, я так понимаю, про механизм отслеживания изменений и перегенерацию только необходимого никто не задумывался и реализовывать не будет?
Я прошу прощения, если ввел в заблуждение. Когда я говрил сервис, я имел ввиду описание создание класса для DI Container как это наприммер сделано в symfony, пример 1, пример 2
maghamed
Если вы хотите добавить кастомную фабрику, т.е. ее логика будет отличатсья от кодогенерируемой. Вы ее просто пишете, и ничего код-генериться не будет.
Мадженто работает по-разному, в зависимости от режима: develop и production
в develop код генерация происходит на лету, в production подразумевается, что вы заранее произвели всю код генерацию и ничего больше генерировать не надо, таким образом производительность будет выше. Т.е. в production режиме не будет механизма по отслеживанию изменений, потому что в нем нет надобности — вы не должны изменять код в решиме production.
Теперь по поводу настройки IoC контейнера (ObjectManager).
В Мадженто система настройки гораздо гибче, чем в Symfony
у нас это di.xml файлы, которые хранят описание
preference, type, virtualType, plugin
здесь можно почитать подробней об этом — http://devdocs.magento.com/guides/v2.0/extension-dev-guide/build/di-xml-file.html
maghamed
мы сделали auto-injection в DI, в отличие от Symfony опять же, чтобы избавить программиста от написания шаблонного кода.
И чтобы он конфигурировал, только то, что ему нужно конфигурировать. А не все зависимости класса.
maghamed
когда пишешь код в режиме Develop (а именно в этом режиме обычно пишут код программисты) нет надобности перегенеривать после любых изменений все код-генерированные сущности.
Достатчоно удалить, только нужные файлы, которые были изменены. И система пересоздаст только их. Не нужно удалять всю папку 'generated' при этом. Это не должно занять много времени.
Вероятно сейчас после каждых изменений Вы запускаете DI компиляцию всего. Этого делать не нужно
CodeKeeper
Интересно, шаблонный код это явно указание зависимости, например, в yml?
В симфони для этого существует parent, который избавляет от внедрения всех зависимостей, а так же повторного вызова методов необходимых для создания класса.
Хорошо, допустим. Почему тогда это нигде не отражено в офф документации? Почему об этом нигде не написанно в той же офф документации? И почему бы не реализовать механизм автоматического отслеживания измененых файлов что бы перегенерировать только измененные код-герерированые сущности? Или как с методами save и load в модели. @deprecated есть, а see нет. И ищи информацию по форумам))) Хорошо что хоть описание в последнюю версию добавили.
maghamed
Да, именно такого кода, который программист обычно copy-paste-ит мы хотим избегать.
Magento по-факту сама вставит вам кастомизированную зависимость в Child, если кто-то кастомизировал ее для Parent, а для Child она не переопределена. Вам для этого не нужно ничего указывать.
Декларацию parent, как в Symfony мы также хотим избегать, во-первых, по причине описанной выше, а во-вторых, потому что Magento не рекомендует использовать Inheritance Based API, т.е. расширение путем наследования в целом. Мадженто для этого предоставляет достатоно других механизмов взамен. И рекомендованным путем является композиция объектов.
Существующие цепочки наследования, например существование Абстрактных: модели, контроллера, блока — можно расценивать как легаси код в системе, который мы пока не убрали в первую очередь из-за требований обратной совместимости, которые мы соблюдаем в 2.* релизах.
Собственно именно поэтому мы сделали deprecated методы save и load на абстрактной модели, как вы заметили.
По поводу документации, сейчас требования к документированию кода очень высоки.
Не зря вы заметили, что добавилось описание к deprecation save и load.
Вот так, например, комментиуется новый код.
Но Magento — это Open Source проект, об этом была моя первая презентация, видео которой тут выложено. Поэтому если Вы видите как Вы можете что-то улучшить в коде или в документации, Вы можете поставить Pull Request, и если он соответствует нашим требованиям, то мы его обязательно приймем, а если не соответсвует, но идея покажется нам полезной — поможем доработать его до вида, чтобы влить его в мейнлайн.
Это касается и автоматического отслеживания изменений и тегов @ see, которые мы пока не бекпортировали в 2.1.*
CodeKeeper
Немного смешно звучит учитывая что при создании модуля мы постоянно делаем рутинные действия, например создание модели, с ресурс моделью и коллекцией.
А это в каком случае? В случае использования Context? В случаях в preference или в случаях virtual types?
Ну для начала parent в Symfony это не более чем механизм который позволяет избавится от дублирующего кода, так же как динамическое создание формы минуя создание класса. Ну и потом, звучит немного странно на фоне того, что при создании модуля мы все так же наследуем базовые классы(раз, два, три, четыре, пять, шесть), при том что ничего плохого в наследовании нет, поскольку композиция не всегда является гибким решением.
Я ждал несто подобное) Увы, пока есть куча работы основной, но как только выдастся минутка обязательно внесу свой вклад. И раз пошла такая петрушка, куда я могу оформить баг, при котором админка мадженты уходит в цыклический редирект?
maghamed
Для большей гибкости и кастомизации, которая предоставит возможность подменять минимальное кол-во логики для стороннего разработчика (а не переписывать весь модуль) мы и делаем это разделение.
Например, вот хороший пример написания новой логики в Magento 2 — новая реализация Inventory.
Вы видите здесь примеры boilerplate кода?
Это во всех случаях.
Например, есть ??класс А, который имеет внешнюю зависимость на XInterface, ?у XInterface есть две реализации ?X1 и Х2.
Есть базовый preference XInerface -> X2
??и есть настройка Type для класса А (XInerface -> X1)??
Когда существует класс B, который наследуется от А, ?то ему прийдет в зависимость для XInerface ?-> реализация X1
Опять же это одно из проявлений Liskov Substitution
Еще раз — сравните сколько вы пишите конфигурации в Magento и сколько бы написали в Symfony для большого проекта. В Magento вы пишите меньше, потому что декларируете только то, что вам действительно нужно сконфигурировать.
Не странно, то что вы указали — это примеры legacy кода, который мы пока не меняем, чтобы не нарушить обратную совместимость в новых релизах и не поломать текущие инсталяции.
Базовые абстрактные класса такие как AbstractBlock, AbstractModel, AbstractController это яркий пример layer super type
Например, вот одна из задача из нашего беклога, которой мы делимся с сообществом, чтобы избавиться от абстрактного контроллера
Eliminate the need for inheritance for action controllers
В самом по себе в наследовании нет ничего плохого, но ошибочно используют наследование очень многие. Наследование вводит самый жестки coupling который только может быть, и потом уже ничем его не подменишь
maghamed
Вы можете завести GitHub issue
Описав проблему и предоставив шаги, чтобы ее воспроизвести и кто-то из команды Magento или из сообщества вам поможет в решении этой проблемы.
maghamed
То что докладчик выбрал механизм консольных команд представленный в Magento 2 (которые действительно расширяют Symfony\Component\Console\Command\Command) не есть проблемой.
В данном примере, новая команда чистит кеш, но при этом отношение is_a по отношению к родительской команде соблюдается.
Класс это тоже интерфейс, точней два интерфейса — один открытый, второй защищенный. Поэтому зависимость на конкретный класс, в месте, которое предполагает расширение в будущем — это плохо, так как программист, который захочет расширить базовое поведение, будет вынужден наследоваться от вашего класса. А это не правильно, так как с высокой долей вероятности вы нарушите принцип Лисков
CodeKeeper
Спасибо за ответ. Пересмотрел класс родитель, да, если прочитать Ваш ответ, так и есть.
Да, но докладчик говорит о нарушении ссылаясь на «конкретную реализации», т.е. ссылаясь на предыдущий пункт Dependency Inversion. И из-за этого складывается ощущение что он сам слабо понимает о чем говорит.
dastus
Спасибо за вопросы.
1. Конечно докладчик понимает что в Magento 2 используется Symfony Сonsole Сomponent, в том числе и в примере приведенном в докладе. Почему для демонстрации реализации выбран именно этот пример? В данном примере по мнению автора удачно выбрана абстракция для наследования, что является основной целью LSP.
2. Я и не говорю что Unit тестирование было запрeщено в Magento 1. Я говорю о том, что в виду архитектуры M1 было сложно организовать действительно изолированное тестирование каждого класса.
3. Не совсем понятен вопрос. Тоесть таки нарушаем но чуть чуть? Это как? Вы проде бы и согласны что построение зависимости на основе реализации, а не на основе абстракций это нарушение принципа OCP, но вроде бы и не согласны. Более того я говорю о том что иногда мы можем отходить от следования данного принципа, но все таки это будет отхождение а не «вопиющее» или не «вопиющие» нарушение. Вопрос в том оправдано ли оно. Если мы знаем что мы никогда не будем менять зависимость, то возможно нам и не нужно создавать зависимость на абстракции. Но в случае Magento 2, когда нам предоставлен механизм указания нужной реализации, я считаю, что все таки стоит избегать таких случаев. Где вычитал — Agile Principles, Patterns, and Practices in C# By Martin C. Robert, Martin Micah (http://druss.co/wp-content/uploads/2013/10/Agile-Principles-Patterns-and-Practices-in-C.pdf).
CodeKeeper
Да в принципе в другой ветке был дан ответ на вопрос по поводу нарушения solid. Magento2 представляет механизм несовсем указания. Посути это просто механизм подмены интерфейса или конкретного класса, поскольку можно написать
preference for="Magento\Framework\Logger\Monolog" type="Coolryan\PreferenceExample\Model\Log"
И спокойно подменить логер.
В любом случае спасибо за ответ.
maghamed
То как вы описали,
Это не рекомендованый подход. В данном случае вы будете использовать механизм Duck Typing.
Который не безопасный и не объектно ориентированный по своей сути.
CodeKeeper
А что именно вы понимаете под теримином «Утиная типизация»?