Недавно мы представили защищенную корпоративную почтовую систему «Mailion. Сертифицированный» — единственную на российском рынке с действующим сертификатом ФСТЭК России. Продукт предназначен для работы с конфиденциальной информацией в крупных коммерческих и государственных организациях.
Речь о сложно устроенной и технологически разнообразной системе: Mailion включает в себя семь крупных модулей, более 400 собственных компонентов (не считая стилевых, вспомогательных и интеграционных обвязок), и содержит в целом почти 400 тыс. строк кода.
Под катом — наш рассказ об устройстве пользовательской части Mailion. Говорим об архитектуре фронтенда и о том, как и почему менялся его стек с начала разработки в 2017 году.
Привет! Меня зовут Роман Животягин, более восьми лет я разрабатываю софт: специализируюсь на JavaScript и TypeScript, владею PHP и C#. В МойОфис руковожу одной из команд разработки Mailion.
На Хабре о создании этой почтовой системы уже писали мои коллеги — советую их статьи (1, 2, 3, 4, 5, 6) в первую очередь тем, кому интересно базовое устройство продукта, его предназначение, бэкенд и тема микросервисов. В этом же материале я расскажу о технологическом пути, который мы с командой Mailion прошли за последние годы в разработке и совершенствовании «фронта».
Из чего состоит Mailion: архитектура и стек
Архитектуру Mailion верхнего уровня можно представить в виде иерархической модульной структуры. Точкой входа в нее служит приложение-оболочка (shell) с общей функциональностью: хранилища, менеджер состояний, общие обработчики и пр. К оболочке подключаются относительно независимые модули, которые содержат собственную функциональность и состоят из переиспользуемых компонентов. Всего модулей семь: профиль пользователя, почта, календарь, администрирование, контакты, справка и настройки. В комплексе они покрывают большинство потребностей, связанных с коммуникациями в крупных компаниях.
Вот как некоторые составляющие системы выглядят со стороны пользователя:
По сути, проект разработки предполагал успешное совмещение нескольких пользовательских сервисов — а следовательно, целого ряда функциональных и адаптивных интерфейсов. В связи с этим мы много размышляли над подбором гибкого стека, которым позволил бы реализовать все планы.
На старте проекта в 2017 году мы выбрали в качестве фреймворка библиотеку Polymer. Основной причиной стало то, что Polymer собрал под капотом различные преимущества веб-компонентов — в виде удобного API и целостной библиотеки, готовой к использованию. К тому же казалось, что Google планирует активно продвигать Polymer в качестве нового стандарта (какое-то время, судя по всему, так оно и было). Но прежде чем продолжить рассказ о фреймворке, предлагаю поговорить о специфике самих веб-компонентов.
Зачем нужен компонентный подход?
Когда я начинал свой путь в разработке, компонентный подход во фронтенде только зарождался. Компании начали имплементировать MVC в виде библиотек и фреймворков, только-только появились AngularJS, Backbone и прочие инструменты. Но зачастую при знакомстве с новыми технологиями бывает вообще непонятно, как с этим работать. Распространенный подход того времени: у нас есть кучка маленьких библиотек, кучка библиотек побольше, есть jQuery, берём всё это и пишем «фронт». При этом HTML по большей части собирался на стороне сервера (то, что сегодня возродилось в концепции SSR), на клиенте же требовалось добавить интерактивности или сделать ajax-формочки.
С тех пор компонентный подход успел стать данностью. Сегодня веб-компоненты выглядят логичным продолжением идеи самодостаточности в компонентах. Речь идет, по сути, о наборе из трёх технологий:
-
Пользовательские элементы (custom elements). Позволяют нам регистрировать собственные компоненты: создавать их, привязывать к ним некое поведение, стилизовать их, произвольно именовать, описывать их программную и визуальную часть, инкапсулируя их внутри компонента.
-
Теневая DOM (shadow DOM). После регистрации кастомных компонентов, мы можем привязать к ним теневую модель. Она позволяет нам изолированно от основной DOM работать со стилями, разметкой и кодом того или иного компонента.
-
HTML-шаблоны (спецификации
<template>
,<slot>
).<template>
дает возможность отложенной генерации какого-то представления;<slot>
же позволяет создать в модели документа врезку, в которой компонент существует независимо от основной части документа.
Теперь вернемся к Polymer, который собрал под капотом все эти технологии, и рассмотрим его ключевые концепции:
-
Миксины. Функция, которая принимает некий базовый класс и возвращает расширенный класс. Пользоваться ей мы стали не сразу, поскольку начинали с Polymer 1.0, в то время как миксины появились в версии 2.0.
-
Поведение. Благодаря миксинам мы можем наследовать функциональность и задавать определенное поведение, которое компоненты могут потом перенаследовать и переиспользовать.
-
Инкапсуляция. Программный код, стили и разметка содержатся в одном компоненте.
-
Ленивая загрузка. Актуальна для больших приложений вроде Mailion, поскольку позволяет пользователю не загружать данные, которые он пока не будет использовать.
-
Шаблон наблюдателя. Вычисляемое свойство, когда мы можем на основе некоторых переменных вычислить другую переменную в одной функции.
-
Двойное связывание. Возможность передавать данные при их изменении как от родительских компонентов к дочерним, так и от дочерних к родительским. По сути, представляет собой синтаксический сахар над прослушиванием событий.
В ходе работы мы выявили следующие недостатки и преимущества Polymer:
Плюсы |
Минусы |
— Простой и понятный API — Все плюшки веб‑компонентов — Простое переиспользование кода — Набор компонентов из коробки |
— Скудная поддержка сообществом — Не всегда очевидное наследование и переопределение стилей — Проблемы с полями ввода: поскольку Polymer это теневая модель, браузеры не очень ее хорошо парсят, и им трудно работать с автозаполнением — Сложность кастомизации сторонних компонентов |
Приходим к микрофронтендам
При разработке Mailion мы остановились на структуре типа «монолит»: есть Polymer и есть монолитный репозиторий, в котором мы хранили компоненты. Постепенно мы наращивали логику, компонентов и подприложений становилось все больше, а затем какой-то момент Google объявила, что прекращает развивать Polymer.
Поскольку у нас уже была большая кодовая база, мы решили рассмотреть другие варианты фреймворков и библиотек. При этом «копали» в сторону взаимодействия фреймворков: например, проводили исследования, как Vue или React будут взаимодействовать с тем же Polymer. В результате поняли, что нам нужны микрофронтенды. Для миграции решили выбрать монорепозиторий с управлением под системой сборки Nx, плюс React и TypeScript.
Сами по себе микрофронтенды — концепция, которая предполагает шаг в сторону большей независимости и функциональности компонентов. Вы делаете независимый функциональный компонент, и можете работать с ним изолированно — в своих приложениях или каких-либо других компонентах. Примеры таких компонентов: сложные формы редактирования, формы заведения сущностей. Скажем, нужно создать форму пользователя, где будет множество полей — аватар, имя, фамилия и прочее; все это можно вынести куда-нибудь на отдельный хост, запустить и работать с этим изолированно вне нашего приложения.
Любой компонент с законченной функциональностью, который можно собирать и использовать независимо или в окружении какого-нибудь приложения, можно назвать микрофронтендом.
Микрофронты предполагают набор важных для нашей разработки возможностей:
-
Разделение больших модулей на независимые микрофронтенды.
-
Упрощение масштабирования приложений при увеличении численности команды разработки.
-
Можно выпускать релизы отдельными частями приложения = >> повышение частоты релизов.
-
Увеличение скорости автотестирования.
-
Разворачивание микрофронта на отдельном стенде.
-
Можно взбалтывать разные стеки, но не смешивать их (гибридное приложение). Мы, например, можем использовать Polymer в оболочке на React без опасений, что что-то будет работать не так.
Основные варианты, на чем можно делать микрофронты:
-
Ванильный JS.
-
Специализированные библиотеки и фреймворки.
-
Мультирепозитории.
-
Монорепозитории.
-
Федерация модулей webpack — когда вы можете разделить код и при этом разместить его на разных хостах.
Наши варианты — монорепозитории и федерация модулей.
Почему мы выбрали React в качестве библиотеки?
Изначально подкупили эти преимущества:
-
Унификация стека технологий. Учитывая широкую продуктовую линейку МойОфис, у нас есть потребность использовать общие компоненты, которые можно переиспользовать. У нас уже есть такие кейсы: например, галерея изображений, которая используется сразу в нескольких продуктах.
-
Большая поддержка сообщества.
-
Множество сопутствующих библиотек.
-
Интеграция с Nx по умолчанию.
Сразу скажу о возможностях Nx — довольно молодой системы сборки, которая решает для нас ряд проблем:
-
Разделение на проекты «из коробки». В терминологии Nx все является проектами. Есть проекты приложения и библиотеки, мы можем их хранить и разрабатывать отдельно, при необходимости можем их публиковать, нам проще их поддерживать.
-
Независимая сборка приложений.
-
Ограничение взаимозависимости проектов через механизм тегирования областей.
-
Генераторы рабочего пространства.
-
Кэширование вычислений. Nx кэширует вычисления как на клиенте, так и распределено: его можно запустить на нескольких узлах, отправить ему задачи, он выполнит все, что вы запланировали, и вернет результат. При этом возьмет из кэша части кода, которые не менялись.
-
Визуализация графа зависимостей. Вы всегда можете наглядно увидеть, какие проекты от каких зависят.
Чем полезно разделение кода
Под капотом Nx содержит сборщики webpack и rollup, мы используем webpack. В базовой конфигурации webpack мы можем разделять код на этапе разработки, но в итоге собираем его в бандл, который затем храним на одном хосте.
Федерация модулей позволяет нам делать очень важную вещь. Мы с самого начала можем запланировать конфигурацию таким образом, что разделим отдельные части — например, оболочку приложения, приложение 1 и приложения 2 — запустим их на разных хостах, и оболочка будет тянуть эти приложения по сети. Мы можем расположить приложения на разных хостах и работать с ними изолированно. Это и есть реализация идеи микрофронтов.
Еще раз о стеке
В заключение остановлюсь подробнее на составе нашего стека технологий. Точнее — стеков, поскольку мы ведем разработку и на Polymer, и на React.
Polymer 2.0 |
React |
— Веб‑компоненты — Redux — IndexedDB — Service Worker — WebSockets — History API — Bower — Electron |
— Nx — TypeScript — React Query — IndexedDB — Service Worker — WebSockets — Electron — History API — MUI — Storybook — Jest — Cypress — husky — Webpack — Yarn |
В перспективе мы нацелены на создание гибридного приложения, и пока что гибрид подняли частично. Так, в оболочку на React у нас уже мигрирована часть компонентов: например, настройки, лейауты, сайдбары и календарь. Отдельным приложением на React реализована авторизация.
Поскольку мы продолжаем расширять команду, в случае с React крайне важен TypeScript: нам требуется надежное согласование интерфейсов, и типизация помогает в этом.
Что касается Mailion на Polymer 2.0 — это пример прогрессивного приложения, где под капотом есть все:
-
Мы используем подход offline first, то есть при отсутствии сети ходим в первую очередь в локальную базу данных (это удобно, например, когда пользователь хочет почитать цепочку писем).
-
Поскольку мы реализуем поддержку миллиона пользователей, а отдавать статику на миллион — не очень хорошо, мы кэшируем через Service Worker те вещи, которые не надо загружать по сети.
-
WebSockets задействуем для нотификаций, History API — для роутинга.
-
Наконец, для настольной версии почты на популярных системах (macOS, Windows, Linux) мы используем Electron.
***
Выше я постарался базово рассказать о том, из чего состоит фронтенд почтовой системы Mailion. В будущих материалах планирую углубиться в отдельные аспекты стека: как минимум подробнее раскрыть роль монорепозитория Nx и веб-компонентов в нашей разработке. Если вам интересны нюансы технического устройства Mailion, связанные с фронтендом, напишите об этом в комментариях — буду рад ответить на вопросы, учесть пожелания и поучаствовать в обсуждениях.
Много интересного вы можете почерпнуть и в других хабр-статьях про разработку Mailion:
-
Первый взгляд: как устроена новая корпоративная почтовая система Mailion от МойОфис
-
Как мы создаём почтовую систему нового поколения Mailion. Архитектура кластера DOS
-
Способ представления числовых ключей для обратного поискового индекса
Если вы талантливый фронтенд-разработчик, любите разбираться в сложных неординарных задачах и хотите реализовывать себя в масштабных проектах, приходите работать в МойОфис! Мы будем с радостью делиться собственным опытом и искать возможности для взаимного развития.
Автор:
romanzhivo
Интересная разработка, мне приглянулся административный интерфейс
Ребята неплохо потрудились над этой почтой. Работаю в ней с удовольствием.