javascript

Выбранный UI-фреймворк – вред. Архитектурные требования – профит

  • суббота, 22 июля 2017 г. в 03:13:00
https://habrahabr.ru/company/netcracker/blog/333734/
  • Программирование
  • ReactJS
  • JavaScript
  • AngularJS
  • Блог компании Netcracker




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

  • Войти в метро теперь – не просто кинуть пятачок, а приложить карту Тройка, записанную на телефон и учитывающую пересадку.
  • Позвонить по телефону и посмотреть телевизор – давно уже не провести два провода в квартиру и вносить фиксированную абонентскую плату, а triple play с кучей опций и возможностей.
  • Посмотреть дневник сына – на святое же покусились! – теперь можно с планшета, заодно ответив на комментарий классного руководителя о его неудовлетворительном поведении.

Ну и я уже молчу про всякие Tinkoff, Apple Pay, Google Now, умные дома и многое другое.

Как следствие, в любой компании растут IT-отделы. То, чем раньше занимались несколько десятков сотрудников, сейчас делают команды из тысяч и десятков тысяч человек (кстати, поделитесь в комментариях, как выросли ваши IT-отделы).

Такие большие команды вынуждены более ответственно подходить к выбору технологий, в том числе и UI-фреймворков. И вот вам вброс: неважно, какой UI-фреймворк выбран. И даже вредно ограничивать себя выбором одного фреймворка. Но абсолютно не вредно и даже необходимо следовать правилам использования этих фреймворков.

Технологическое развитие – увеличение скорости, объема и удобства обмена информацией – позволило большим IT-коллективам не собираться в одном здании, а работать распределенной командой. И вот уже у нас огромные open-source проекты, где много команд из различных компаний, стран и континентов работают над одним продуктом.

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

  • Декаплинг. Строгое разделение на back-end и front-end. REST-сервис под каждый отдельный UI.
  • Микросервисы. Каждая команда независимо разрабатывает свою часть, но все эти части должны сложиться в одно работающее приложение.

Эти особенности фактически развязали руки front-end разработчикам и позволили разным командам выбирать front-end технологии в зависимости от бизнес-задач, экспертизы команды, предпочтений и «религии».

Во многом поэтому мы сейчас наблюдаем активный рост и отсутствие явного и безоговорочного лидера в UI-фреймворках: AngularJS, ReactJS, EmberJS, ExtJS и др. При этом, вполне вероятно, надо быть готовым к тому, что завтра появится новая front-end технология, которая будет более эффективной и будет более активно поддерживаться/развиваться сообществом. Именно поэтому считаю неправильным сейчас делать долгосрочный выбор и останавливаться на одной front-end технологии. Более того, вести разработку надо так, чтобы front-end технология была относительно дешево заменяемой, всегда держать это в голове при разработке архитектуры конкретного приложения. Это задача номер один.

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

Для решения этих задач мы, front-end практика в компании Netcracker, разрабатываем архитектурные требования, которые обязательны для всех продуктов.

Немного теории модульного подхода


В основе архитектурных требований лежит подход, который подразумевает независимые JS-блоки и их встраивание друг в друга. У этого подхода есть несколько названий: Scalable JS Apps, Modular JavaScript, JS Portals. Такие фреймворки сравнительно молоды и, можно сказать, сделаны по принципам Николаса Закаса: если можете писать, просто пишите монолит, если нужна интеграция с различными блоками, то придется использовать модульный подход.

Терминология


  • Application (конечное приложение) – набор html/js/css, который отображается пользователю. Конечное приложение может в общем случае не использовать принципы Modular JavaScript и быть написано на любом языке, хоть на flash.
  • Application Controller – JS-объект, реализующий принципы Modular JavaScript, позволяющий модулям общаться и встраиваться рядом и друг с другом.
  • Module – JS-приложение на любом языке, подобном JS.
  • Sandbox – объект, через который Module может общаться с внешним миром.
  • Service – утилитарные объекты, имеющие логику, но не имеющие UI.
  • BroadcastService – сервис, позволяющий модулям общаться.
  • Plugin – встраиваемый в Application Controller модуль. Application Controller может использовать его, чтобы получить новый функционал.



Modular JavaScript. Принципы


Принципы основаны на том, что мы считаем каждый модуль ребенком. Поэтому к ним применимы следующие правила:

  1. Модуль может вызывать только свои методы или методы Sandbox.
  2. Нельзя смотреть на DOM вне своего Sandbox.
  3. Нельзя трогать ненативные глобальные переменные.
  4. Если модулю что-нибудь нужно, это нужно спросить у Sandbox.
  5. Не разбрасывать игрушки (модуль не должен создавать глобальные переменные).
  6. Не разговаривать с незнакомцами (модуль не должен напрямую вызывать другой модуль).

Оригинал можно прочитать здесь.

Эти принципы реализованы Николасом Закасом в ряде проектов, например здесь: http://t3js.org. Однако есть альтернативные реализации: http://openf2.org/ (хотя принципы там те же, о них рассказывается в видео).

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

Наша специфика и архитектура


Уточнения и дополнительные принципы, которые диктуются спецификой наших проектов:

  1. Сервис не имеет UI;
  2. Сервис обычно singleton;
  3. Модули могут общаться через сервис общения (это может быть EventBus или publish/subscriber);
  4. Есть только один сервис общения, общий для всех модулей;
  5. Поскольку мы имеем дело с JS, нужен статический анализатор кода (например, ESLint), который запретит внесение изменений в код в случае нарушения принципов;
  6. Реализация Modular JavaScript должна быть JS Agnostic, т. е. модуль может быть написан на любом языке, подобном JS;
  7. Необходима поддержка модуля в модуле, так как часто хочется переиспользовать код (например, модуль table может быть отрисован внутри модуля dashboard, который, в свою очередь, рисуется на модуле tab navigation – наподобие панели c вкладками для переключения);
  8. Из-за того, что модуль не может выйти за рамки своего элемента, необходим DialogManagerService, который управляет body для показа диалогового окна; модуль, который хочет показать диалоговое окно, использует модуль dialog и передает его в сервис;
  9. Поскольку модулей в проекте может быть много, компилирование всех в один пакет или предварительное соединение ссылками может привести к проблемам с производительностью, поэтому модули должны уметь подгружаться асинхронно по требованию и, естественно, запускаться только после того, как подгружены все другие модули и сервисы, от которых они зависят; отсюда следует, что нам понадобится устранять конфликты между зависимыми модулями.

И вот какая получается архитектура



Блок T3-NC-powered представляет собой Application Controller. У Application Controller есть набор базовых плагинов (треугольники), которые дополняют его функциональность. Плагины не имеют доступ к Application Controller. Наиболее важным является плагин, позволяющий общаться с сервером для «ленивой» загрузки модулей.

Сервисы могут обмениваться данными с Application Controller. Наиболее важным является сервис для обмена сообщениями между модулями. Также сервисы могут использовать плагины. Сервисы не имеют UI, так как предоставляют JS-функции.

При старте модуля по его имени создается Sandbox, который ограничивает модуль. Основной API – дает возможность получить сервис и DOM element, в который модулю нужно встроиться. Если необходимо запустить два модуля с одинаковым именем, то Application Controller создаст два экземпляра модулей, внутренние id которых будут разными.

Заключение


Совместная разработка большой распределенной командой принесла декаплинг и микросервисы, что развязало руки front-end разработчикам и позволило разным командам выбирать различные front-end технологии. Это могло бы породить хаос и превратиться в бесконечный спор.

Но мы хитрые ) Модульная разработка и архитектурные требования позволяют собирать в одно UI-приложение части, разрабатываемые различными front-end командами, и гарантируют относительно дешевую замену front-end фреймворка.

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