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

Сегодня — это руководитель и senior fullstack Марк Локшин. В этой беседе мы обсуждаем о заходе в тему разработки собственного инструмента Business Intelligence. Тема недвусмысленно намекает на то, что данная тема уже не раз была описана на соответствующих ресурсах, а российский рынок даже после ухода с него западных вендоров обладает собственным набором вполне зрелых и рабочих решений.

Собственно, откуда у нас появилась такая задача? Конечно же от заказчиков. А заказчики у нас чаще специфические: государственные и около организации, администрации субъектов регионов. У этих «ребят» чаще всего основная задача показать большому начальнику на совещании красивый график и отчитаться, как же все классно поработали, у особенных из этих заказчиков предъявлены жесткие требования к инструментам разработки.

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

Если проще, то на примере отчетности по иерархии снизу вверх субъектов региона региональному центру, каждый отправляет свои данные в произвольном виде — кто‑то по почте в excel‑файликах, кто‑то через ftp‑шары, а кто‑то просто устно докладывает по телефону. В итоге отдельно выделенные секретари всё сводят в опять же excel, а потом вываливают начальнику вообще в PowerPoint. А у кого‑то уже есть частичные решения по визуализации данных или свой кое‑как отлаженный механизм ETL, поэтому переходить на комплексное платформенное решение как минимум задача избыточная, а как максимум — просто не хочется.

В итоге мы довольно быстро пришли к модульной архитектуре, в рамках которой каждая отдельная функциональность должна уметь «жить» отдельно от остальных. Мы выделили следующие базовые (и естественно замещаемые) блоки:

  • ETL

  • Хранилище данных

  • Модуль визуализаций

  • Модуль разграничения прав

  • Интеграции

Для кого?

Здесь «Америку открыть» ни для кого не получится. Как и любая BI‑система наша должна уметь «обслужить» 3 глобальных типа людей:

  1. Те, кому надо «посмотреть», т. е. конечные потребители визуализируемой статистики;

  2. Аналитики, которые для первого типа пользователей, должны эти визуализации настраивать;

  3. Администраторы и data‑аналитики, которым уже имеющиеся на предприятии куски системы, нужно «подружить» с нашими, в случае частичной поставки описанных выше блоков.

С первым типом пользователей всё довольно просто. Их интересует чтобы было понятно и наглядно. В качестве основы для визуальных компонентов были взяты библиотеки ChartJS и ECharts, а для лучшей идентифицируемости был выделен ещё один не очень стандартный уровень абстракции данных (про это немного позже).

Аналитики. Причем чаще всего именно бизнес‑аналитики. Вот для этих ребят мы старались, как могли. В первую очередь начальство поставило цель — аналитик не должен писать код. В итоге, чтобы не нагружать нежные умы, привыкшие в лучшем случае к PowerBI и Excel, мы реализовали полностью NoCode‑редактор виджетов.

Руководствуясь опытом использования таких решений, как Apache Superset и DataPine, у нас получилось где‑то за 2 месяца реализовать 2 простых в использовании редактора: виджетов и дашбордов. Основным отличием от конкурирующих (да и исходных) решений был тот самый ещё один уровень абстракции. Само по себе понятие виджета было переосмыслено. Виджетом мы назвали набор компонентов с данными для них, распределенных по настраиваемой сетке. «Коллеги по BI‑цеху» в свою очередь обычно подразумевают под виджетом один компонент (график, фильтр, таблицу и т. п.).

Вот так в итоге выглядят наши дашборды:

А вот так настраиваются отдельные виджеты:

И наконец последний третий тип пользователей. Те, кто управляют потоками данных, проще говоря, настройщики ETL. Если такой тип пользователей вообще есть у заказчика, то считай 30% успеха у тебя в кармане уже лежит. Этим ребятам не так важен NoCode/LowCode, сколько возможность написать свой произвольный скрипт в любом месте цепочки трансформации данных. За основу, и как один из рекомендуемых вариантов использования, ETL‑инструментария был взят Apache Nifi. Кто сталкивался с данным инструментом, тот понимает, что он умеет делать чуть меньше, чем всё, что взбредет в голову аналитику данных.

Казалось бы — зачем изобретать что‑то ещё? За ответом стоит обратиться к началу данной статьи. Apache Nifi реализован на Java, которую «люди в зеленой униформе» на дух не переносят. Ответом стал Community‑форк движка Nifi, переписанный на C++ — Apache Minifi. Если коротко, то это почти то же самое, что и Nifi, только без визуализации. Изначально, создателями данного приложения явно не ставилась задача стать главным движком ETL‑системы, но мы начали использовать его именно так — со всеми вытекающими плюсами и минусами, соответственно. Изобретая опять же по минимуму новых велосипедов, мы прикрутили к новому зарождающемуся фронту приличное логирование, редактор python‑скриптов и мониторинг утилизации ресурсов. Всё остальное было перенесено «reverse engineering» практически без изменений из Nifi.

Конструкторы контента

Учитывая вышеописанные типы пользователей, мы приняли решение разделить инструменты создания контента в рамках всей системы. Данный подход решает несколько задач:

  • Управление правами доступа к разным элементам настройки;

  • Гибкое разграничение ролей среди сотрудников, ответственных за настройку;

  • Не делает единый интерфейс перегруженным разнородными функциональностями.

Ниже будут приведены получившиеся конструкторы настроек, в соответствии с порядком «перетекания» данных от одного к другому:

  1. Конструктор подключений. В рамках него настраиваются подключения к обычным реляционным СУБД. Так же мы заранее предусмотрели кейс, когда данные хранятся не в обычной СУБД, а например в заранее подготовленном хранилище, таком например, как Apache Kylin. Настройка подключения к подобным инструментам осуществляется в том же интерфейсе, но соответственно по специфическому интеграционному API.

  2. Конструктор OLAP. Имея зарегистрированное подключение к БД, мы получаем получаем её структуру и имеем возможность вытащить только те данные, которые нам нужны и описать связи между ними. Результатом этого «надергивания» будет набор псевдо‑таблиц (лукапов) и связей между ними. Таким образом мы реализовали ROLAP‑кубы, что подразумевает не хранение непосредственно данных, а только связей между ними.

  3. Конструктор виджетов. Как я уже писал выше, под виджетом мы понимаем не просто график, таблицу или фильтр, а сгруппированный набор компонентов для отображения или фильтрации данных. В рамках создания виджета пользователь‑настройщик задает внутреннюю сетку виджета и «кладет» в её ячейки уже непосредственно компоненты для визуализации и фильтрации данных. Для каждого компонента источники данных настраиваются по отдельности, что на первый взгляд может показаться громоздким, но фактически дает большую гибкость при настройке. В качестве источников данных подразумеваются сущности, созданные в рамках пунктов 1 и 2.

  4. Конструктор панелей. Собственно, это уже сборка конечных дашбордов из приготовленных в пункте 3 виджетов. Сразу нужно оговорится, что данный подход дает возможность реиспользовать один и тот же виджет в рамках нескольких дашбордов.

  5. Конструктор меню. Последним конструктором, фактически формирующим структуру пользовательского приложения для просмотра данных является конструктором меню. Это довольно простая, но крайне полезная вещь, позволяющая организовать структуру и иерархию пользовательского приложения, основываясь в основном на уже созданных в пункте 3 панелях. Помимо панелей мы предусмотрели возможность «вешать» на пункт меню ссылки на внутренние (для специфических «костыльных» функциональностей) и внешние (простой переход в другие системы) ресурсы.

Внимательный читатель в этот момент спросит — а где же среди этих конструкторов ETL? Ответ простой — Конструктор ETL — это отдельное приложение со своим назначением. Как уже говорилось ранее, модульность и отторгается отдельных блоков платформы позволяет решать только нужные конкретному заказчику задачи, не заставляя покупать всё решение целиком.

Принципы разработки

Конструкторы контента — это фактически инструментарий, разработанный нашей командой в довольно сжатые сроки. Отдельно стоит рассказать о том, какие подходы к разработке мы выработали на старте и стараемся придерживаться их до сих пор.

Метаданные

Создаваемые пользователями артефакты, такие как виджеты с их настройками, панели, из виджетов состоящие и описание кубов мы решили не раскладывать на громоздкие реляционные модели. Каждый артефакт представляет из себя законченную, ценную в т.ч. отдельно и реиспользуемую сущность. Данную сущность проще всего описать в простом json‑файле, который в свою очередь держать либо «под ногами» сервера приложений, либо непосредственно в СУБД (мы пошли через этот вариант). У данного подхода есть как набор очевидных плюсов, так и минусов. Из плюсов, которые мы для себя выделили:

  • Простота описания;

  • Простые CRUD‑АПИ, отвечающие исключительно за взаимодействие с хранилищем метаданных;

  • Фронт получает только ту информацию, которая нужна для отрисовки.

Ну и конечно грабли, на которых мы успели знатно поскакать:

  • Рост функциональности линейно влияет на объем формируемых json. Глазами такое уже практически не разобрать (а иногда это бывает полезно);

  • Хоть это и не наш подход, но всё же для некоторых задач необходимо связывать наши артефакты между собой, а искать в рамках БД по контенту json‑ов, мягко говоря, слабо оптимизируемая задача.

LowCode/NoCode

В первую очередь это касается автоматически генерируемых SQL запросов к данным. Пользователь, настраивающий визуализации не обязан глубоко разбираться в структуре хранения данных и писать многоэтажные join-запросы. При формировании условий для отображения например графика проще дать три поля для обозначения фактов, мер и группировок, куда пользователь будет просто drag&drop-ом набрасывать нужные столбцы таблиц.

То же касается и формирования OLAP-кубов. Для облегчения жизни настройщика проще реализовать access-подобный визард, дополняемый при необходимости простыми where-условиями для обрезки данных.

WYSIWYG

Почему‑то не самый популярный последнее время термин, но, на мой взгляд, актуальности не теряющий.

В любой момент при сборке артефакта, пользователь должен видеть, что у него получается. Поэтому сформированные на виджете запросы к БД выполняются сразу же при формировании или изменении, сохраняются в in‑memory storage и легко «подтягиваются» при вызове из приложения для просмотра.

То же касается и положения элементов интерфейса, как при формировании виджетов, так и при настройке дашбордов. Аналитик формирующий визуализацию заранее видит то же самое, что «попадает на стол» его начальнику.

Дизайн-система и отступления от неё

Зачем нужны дизайн системы — тема, заслуживающая отдельной статьи (тысячи их). В нашем случае чтобы не писать кастомные визуальные компоненты под каждый чих мы взяли палитру компонентов PrimeNG, стилизовали её под требования дизайнеров и «досыпали» с десяток своих уникальных стилистически похожих на остальные компонентов.

Как уже писал выше, для графиков мы использовали такие библиотеки, как ChartJS и ECharts, опять же обложив их своими стилями и плагинами. Так же для использования на графиках мы реализовали несложный алгоритм раздачи цветов «по порядку».

Нужно упомянуть, что наличие сформулированной и реализованной дизайн‑системы не исключает отступлений от неё. Это касается, как сторонних контейнерных реализаций, таких как готовые web‑компоненты или iframe, которые заказчик может пожелать интегрировать в своё решение, так и блока инфографики, который чаще представляет из себя реализованные отдельно страницы‑отчеты, нежели хитрый набор иконок, сделанный для визуализации какой‑то отдельной задачи.

По итогу, данный подход сильно сэкономил трудоемкости на разработку фронта и макетирование форм.

Интеграции

Больная тема любого платформенного решения — это интеграции. Если речь идет о получении сторонних данных, то тут всё более менее просто. Есть модуль ETL, сама идеология которого призвана решать эту задачу.

Но есть (и довольно часто) ситуации, когда сама платформа должна отдавать кому‑то информацию из себя или использовать сторонние сервисы, такие как например в нашем случае прогнозирование.

Не могу сказать, что наше решение получилось элегантным, но тем ни менее рабочим. Во‑первых под каждую специфическую функциональность в рамках системы всё же пришлось реализовывать свой «костыль». Для управления этими подпорками мы, уже во‑вторых, реализовали небольшую табличку настроек, куда без разбора валим все key‑value свойства, необходимые для работы в интеграции со сторонними сервисами. Ну и в‑третьих по наличию value в данной таблице мы определяем показывать в интерфейсе ту или иную возможность или нет.

Средства разработки

Думаю, не надо объяснять, что расползание по разномастному стеку технологий в рамках одного решения — это плохо. Для нас выбор был очевиден, учитывая специфику прошлой деятельности команды разработки. А были мы в основном фронтами‑ангулярщиками. Поэтому, быстро стало понятно, что чтобы начать разработку текущим составом, в основе должен быть back‑end на основе NodeJS‑микросервисов. Был небольшой холивар на тему фреймворка в выборе между Nest и Knex, но он уже менее критичен.

В качестве хранилки для метаданных взяли банальный PostgreSQL. Причем, опять же учитывая специфику сертификации министерства обороны именно версию 9.6.

В основу реляционного хранилища для данных, обработанных ETL‑модулем, был положен такой громоздкий инструмент, как GreenPlum, по сути являющийся кластером из нескольких PostgreSQL. Выбор на него пал опять же по причине потенциально простой сертификации у «особенных» заказчиков и хорошей производительности на большом объеме данных. Так же GreenPlum обладает своей встроенной функциональностью для оптимизированной работы с OLAP, но к своему стыду скажу, что пока возможности исследовать её возможности не было.

Как HTTP‑сервер был использован обычный Apache, что сейчас может звучать уже не очень прилично, но тем ни менее, ссылаясь на всё те же требования «особенных» заказчиков, приходится констатировать только факт. В оправдание подобного решения, отмечу, что оно не влияет практически ни на что, т.к. с задачей отдачи статики фронта он успешно справляется.

Также, для тех кто будет решать задачи разработки платформенных решений с нуля, могу дать отдельный совет. Мы организовали все куски решения в рамках всего трёх монорепозиториев. Кто‑то может сказать, что монорепозитории — это блажь для разработки, но в ситуации отсутствия разграничения на back и front, т. е. когда вся команда в результате выбранного технологического стека является full‑stack, это хорошо экономит время на переключение между проектами. Соответственно, управлять собираемыми артефактами приходится уже на стадии pipeline сборки и выкладки.

Выводы

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

Реиспользование

Не изобретайте велосипедов с нуля. Скорее всего всё, что вам нужно сделать, уже сделано до вас. Но не пытайтесь украсть чужое решение. Реиспользуйте подходы и идеи.

Модульность

Дробите свое решение на максимально отторгаемые куски. Это может быть полезно, как с точки реиспользования чего‑то готового, так и с точки зрения тренда на микроприложения и микросервисы. Иными словами Make PBC (Packaged Business Compatibility) great again!

Общий стек

Ограничьте команду разработки MVP максимум 10 людьми, живущих в одном технологическом стеке. От зоопарка технологий вы максимум получите только маркетинговое преимущество, да и то сомнительное. Все интеграции осуществляйте через данные или АПИ.

Комментарии (1)


  1. GeorgeNordic
    17.05.2024 10:13
    +1

    BI для одного заказчика или на рынок выйдите? По сути вопроса - уже все создали... 83 системы @GromovBI насчитал :)