Skip to content

Instantly share code, notes, and snippets.

@greabock
Last active July 25, 2019 10:27
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save greabock/f06bc85cb2659b7d474b to your computer and use it in GitHub Desktop.
Save greabock/f06bc85cb2659b7d474b to your computer and use it in GitHub Desktop.
События и команды

Как оставаться упоротым и счастливым

Добра!

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

Модули, как они есть

Прежде всего, хотелось бы вернуться к тому, как устроены модули (они же домены, или области ответственности). В прошлый раз, я говорил о том, что можно просто удалить любой модуль и приложение продолжит свою работу. В действительности, это не совсем так. Дело в том, что модули очень похожи на пакеты composer (собственно ими они и могут являться). Что это значит? Это значит, что модули, подобно пакетам, имеют зависимости. Например, модуль Blog может иметь в зависимостях модуль User - ведь у блога должен быть автор. Модуль Image(который отвечает за работу с изображениями) может иметь в зависимостях модуль Storage (который отвечает за работу с файлами). И так далее... Таким образом, мы приходим к такой структуре, когда мы имеем не плоский каталог модулей, а дерево зависимостей, хотя сами модули продолжают лежать в каталоге на одном уровне, как и в уже упомянутом composer.

От инсталляторов к интсалляторам миграций

После множества съеденных собак раздумий над тем, как устанавливать и удалять модули, я пришел к выводу, что инсталляторы в чистом виде (которые я изначально использовал) - это зло. Потому, как они не вписываются в поток миграций - а значит невозможно поддерживать актуальную структуру данных в различных средах, при коллективной разработке. Теперь, вместо инсталляторов, я использую инсталляторы миграций. При использовании команды artisan some-modlue:install модуль копирует из папки своих стабов миграцию в основной поток и устанавливает на ней текущую дату. А при команде artisan some-module:uninstall, копируется "реверсивная" миграция, которая делает обратное действие. Однако, не все так просто...

И снова зависимости

Дело в том, что простая инсталляция модуля может привести к чему нибудь не очень хорошему, если зависимости для установки модуля не разрешены (то есть один из модулей, от которых зависит текущий, еще не установлен). Таким образом, команда artisan some-module:install должна приводить к запуску установки всех зависимостей. Установщики зависимостей, в свою очередь должны выполнить проверку, и если они уже установлены, то проигнорировать эту команду. А во время выполнения команды artisan some-module:uninstall все зависимые модули так же должны быть удалены. Как же разрешить эту проблему? Если в первом варианте все понятно - модуль сам знает, от чего он зависит, и перечислить зависимости, которые необходимо установить не составит труда, то во втором варианте модуль понятия не имеет о зависящих от него других модулях. И здесь нам на помощь спешит система событий...

События и команды

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

А сводится все к тому, что события вызываются в одном месте, а обработаны могут быть во многих. В то время, как команды наоборот -обрабатываются в одном месте, а вызваны могут быть из многих. Вернемся к нашим модулям. При инсталляции любой модуль, даёт команду всем своим зависимостям установиться, все его зависимости перед установкой так же вызывают эти команды для своих зависимостей. Это связано с тем, что модуль знает все свои зависимости, и знает о существовании этих команд. А вот при удалении модуль не знает о том, какие модули от него зависят, и модуль поджигает событие я_собираюсь_удалиться, а уже зависящие от него модули, подписываются на это событие, и удаляются, перед этим сами поджигая событие я_собираюсь_удалиться. Таким образом всё дерево (или ветка) модулей может быть установлено рекурсивно снизу вверх, или удалено рекурсивно сверху вниз.

Взаимодействие между модулями

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

  1. Зависимые модули могут вызывать команды зависимостей.
  2. Зависимости вызывают события на которые могут быть подписаны зависимые.
  3. Зависимость не может рассчитывать на исполнение обработки события - она не знает о наличии или отсутствии обработчиков.
  4. Зависимость обязуется исполнять команды вызванные зависимыми модулями.

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


Надеюсь, что заметка была интересной ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment