javascript

Make Data Visualization Great Again (или как мы создавали свой инструмент визуализации данных)

  • среда, 2 апреля 2025 г. в 00:00:06
https://habr.com/ru/companies/zyfra/articles/896302/

Всем привет! Меня зовут Олег Косарев. Я – ведущий инженер-программист. В группе компаний «Цифра» занимаюсь развитием и поддержкой продукта «Диспетчер». Представляю команду «ОСР» (общесистемное развитие). Моя основная специализация – Frontend-разработка.

В данной статье я хочу поделиться с вами нашим опытом по созданию MVP (Minimum Viable Product, «минимально жизнеспособный продукт») редактора аналитических панелей.

Дисклеймер: Данная статья — своего рода «road movie», рассказывающий о нашей работе за прошедший год. Я старался обойтись без излишних технических подробностей, чтобы сделать материал максимально доступным и понятным. Статья будет полезна Frontend-разработчикам, которые увлечены своей профессией, а также тем, кто работает с BI-инструментами. Кроме того, я надеюсь, что она сможет помочь тем, кто выгорел и находится в поисках вдохновения. Ведь (спойлер) это история о преодолении трудностей и достижении целей.

Цифра. Диспетчер

Для начала хотелось бы сказать пару слов о комплексе «Диспетчер». Это программное решение, предназначенное для контроля производственных процессов и мониторинга оборудования на промышленных предприятиях. Система построена на автоматическом сборе объективных данных (IoT) и современных технологиях Индустрии 4.0.

Аналитические панели (дашборды) в системе «Диспетчер» — это интерактивные визуализации данных, которые помогают операторам, диспетчерам и руководителям в режиме реального времени контролировать ключевые показатели работы оборудования и персонала. Они предоставляют удобный интерфейс для анализа информации, принятия решений и оперативного реагирования на изменения.

Поехали!

Для реализации проекта была сформирована небольшая команда: продакт-менеджер, тимлид и два разработчика. На всё про всё нам потребовалось 20 спринтов или 10 месяцев в пересчёте на реальное время: от момента первого коммита до момента внедрения редактора в основное решение «Диспетчер.MDC» и замены части существующих (стандартных) дашбордов на наши собственные.

А теперь по порядку, как всё начиналось. В нашем продукте уже «жили» дашборды, построенные с помощью DevExpress Business Intelligence Dashboard. Возникает справедливый вопрос: для чего нам потребовался свой собственный инструмент визуализации данных? Проще всего это объяснить сравнением в таблице.

Pros and cons

DevExpress Dashboard

Наше решение

Польза для заказчика

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

Возможность добавления любых компонентов и  функциональности.

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

Создание аналитических панелей — достаточно сложный и трудоемкий процесс (текущий редактор создан для использования в различных сферах).

Упрощенный интерфейс. Отказ от компонентов и функциональности, которые не использовались нами в редакторе DevExpress (новый редактор «заточен» исключительно под наши нужды).

Снижение трудоемкости создания аналитических панелей под нужды заказчиков. Ускорение оказания технической поддержки.

Отличие компонентов и  интерфейса продуктов комплекса «Диспетчер» и редактора аналитических панелей.

Унификация компонентов, интерфейса редактора  аналитических панелей и  продуктов комплекса «Диспетчер».

Упрощение взаимодействия с системой в целом. Единый дизайн компонентов и интерфейса.

Декомпозиция

Хорошо, допустим, что разработка собственного BI-инструмента для нас вполне оправдана. Но как подступиться к его реализации? Начали по старинке — с анализа конкурента. Мы изучили, как был организован DevExpress Dashboard и выделили следующие его составные части:

  • Макет (*);

  • Панель компонентов (*);

  • Конструктор запросов (*);

  • Контекстное меню компонента (настройки);

  • Редактор формул;

  • Настройки цветовой схемы.

Здесь (*) отмечены, те составляющие, которые мы посчитали обязательными для этапа PoC (Proof of Concept процесс проверки и демонстрации жизнеспособности идеи или технологии до её полной реализации).

Рабочее пространство редактора дашбордов DevExpress
Рабочее пространство редактора дашбордов DevExpress
Конструктор запросов дашборда DevExpress
Конструктор запросов дашборда DevExpress

После декомпозиции, нарезки эпиков на отдельно взятые задачи и ОЧЕНЬ приблизительной оценки в часах по каждому разделу мы приступили к разработке.

Лирическое отступление

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

Стек

После того как мы определились, что нам нужно сделать, пришла пора понять, как мы это сделаем. Это я сейчас говорю про наш стек технологий.
 Для клиентской части редактора мы выбрали «проверенную в боях», уже укоренившуюся в «Диспетчер.MDC» связку React + Redux. Со стороны Backend безальтернативным вариантом для нас был ASP.NET Core. Но на этом простые выборы для нас закончились. Потому что все дальнейшие решения относительно выбора фреймворков и библиотек основывались на проверке их соответствия нашим требованиям для выполнения конкретных задач.

Макет

Разберём этот момент на примере макета дашборда. Макет — это база. Основа. Первая задача, которую мы должны были решить. И уже на этом этапе перед нами предстало огромное количество npm-пакетов, из которых необходимо было сделать выбор.
 
Мы остановились на React Grid Layout, так как с этой библиотекой можно легко создавать динамические и адаптивные макеты, при этом работать с ними просто и удобно. Звучит здорово, да? Но прежде чем добавить зависимость в наш package.json, нужно было проверить, что мы сможем:

  • Динамически изменять размер элементов макета;

  • Перемещать элементы макета;

  • Иметь различные уровни вложенности макетов, группировать их (задача со звёздочкой);

  • Кастомизировать поведение макета, задавать собственные правила добавления и удаления элементов макета (ещё одна задача со звёздочкой);

  • Подстраиваться под различные разрешения экрана.

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

Макет, созданный в редакторе дашбордов «Цифры»
Макет, созданный в редакторе дашбордов «Цифры»

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

Графики

Итак, у нас уже есть рабочий макет. Что с его содержимым? Раз мы создаем редактор дашбордов, очевидно, нам понадобятся графики. Как и в случае с библиотекой для макета, мы столкнулись с выбором инструмента для их построения. Вновь — масса возможных вариантов. Но нам необходимо было найти библиотеку, которая:

  • Даёт возможность строить из коробки все необходимые нам типы графиков (линии, бары, области, возможность комбинировать эти типы на одном графике);

  • Предоставляет широкие возможности для настройки компонентов;

  • Обеспечивает необходимый уровень интерактивности;

  • Проста в использовании;

  • Имеет хорошую документацию;

  • Имеет большое сообщество;

  • Активно развивается (релизы публикуются на регулярной основе).

В итоге, после продолжительных поисков наш выбор пал на библиотеку Recharts, основанную на D3.js. Так как графики — лицо любого редактора дашбордов, то следует сказать пару слов об этом инструменте.

Одним из ключевых преимуществ Recharts является её глубокая интеграция с React, что позволяет максимально эффективно использовать компонентный подход. Благодаря этому, созданные на Recharts графики легко комбинировать и переиспользовать.

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

Библиотека достаточно популярная и имеет большое комьюнити. На момент написания статьи недельная загрузка на npm составляла — 3 083 195.

Внедрить Recharts нам было довольно легко, на большинство возникающих вопросов мы быстро находили ответы в документации и интерактивных примерах на странице инструмента. Не могу сказать, что Recharts не имеет изъянов. Этот инструмент нельзя назвать идеальным решением для рендеринга графиков. Тем не менее, он весьма функционален и прост в освоении.

Графики, созданные в редакторе дашбордов «Цифры»
Графики, созданные в редакторе дашбордов «Цифры»

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

Конструктор запросов

Теперь, когда у нас есть макет и возможность добавлять графики, пришло время заменить mock-данные на реальные. Следующий этап — подготовка редактора к извлечению данных из базы. Для этого нам потребовалось создать конструктор запросов.

Конструктор запросов — инструмент, который даёт возможность пользователю только при помощи интерфейса без написания кода подготовить SQL-запрос к базе данных.

Основную его часть занимает рабочее поле, на которое добавляются элементы из списка доступных таблиц и представлений. Это как раз те самые таблицы в базе данных, с которой установлено соединение. Пользователь может выбирать, какие поля должны войти в итоговую выборку и устанавливать различные типы связей между таблицами.

Например, конфигурации:

соответствует следующий SQL-запрос:

SELECT
  "Tbl0"."code" AS "machine_code",
  "Tbl1"."name" AS "machineparam_MachineParam_Name"
FROM "dbo"."machine" AS "Tbl0"
INNER JOIN "dbo"."paraminmachine" AS "Tbl2" ON "Tbl2"."machineid" = "Tbl0"."id"
INNER JOIN "dbo"."machineparam" AS "Tbl1" ON "Tbl1"."id" = "Tbl2"."machineparamid"
LIMIT @p0

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

Как мы всё это осилили? С точки зрения Frontend-разработчика стояли следующие подзадачи:

  1. Необходимо реализовать представление конструктора в качестве диаграммы (графа);

  2. Обеспечить возможность добавлять таблицы БД в качестве узлов;

  3. Обеспечить возможность создавать связи между узлами (таблицами);

  4. Обеспечить возможность взаимодействия с конкретными узлами (задание настроек самой таблицы, задание настроек полей таблицы);

  5. Обеспечить возможность взаимодействия со связями между узлами (опять же настройки);

  6. Конвертировать получившийся в результате граф в объект, который будет отправлен на сервер для вычисления итогового запроса.

Большинство проблем нам помог решить React Flow. Это библиотека React-компонентов для создания и управления графическими диаграммами (графами). Она предоставляет удобные инструменты для визуализации сложных структур, позволяя пользователям взаимодействовать с элементами графа, перетаскивая узлы, соединяя их связями и управляя их состоянием.

Таким образом, сразу же была закрыта основная часть проблем реализации конструктора запросов. Мы убедились на практике, что React Flow предоставляет удобный API для настройки узлов и сбора данных по ним. С таким инструментом, настройка таблиц и полей с последующей конвертацией всего графа в JSON уже не представлялось сложной задачей.

Вот здесь доступно небольшое демо: Codesandbox.

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

Забегая вперёд, скажу, что всего нами было реализовано 16 различных компонентов дашборда и НЕСЧЁТНОЕ количество настроек для их отображения. Останавливаться на них отдельно не будем, просто посмотрим что у нас получилось:

Дашборд «Контроль регистрации работников», интегрированный в «Диспетчер.MDC»
Дашборд «Контроль регистрации работников», интегрированный в «Диспетчер.MDC»
Дашборд «Контроль загрузки оборудования», интегрированный в «Диспетчер.MDC»
Дашборд «Контроль загрузки оборудования», интегрированный в «Диспетчер.MDC»

Ретро

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

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

  • Планирование: Планирование зачастую является уязвимой точкой в процессе разработки. Чтобы оценить свои силы, а уж тем более в часах, нужен опыт. В начале такого специфического в предметной области опыта у нас, разумеется, не было. Он приходил со временем в процессе работы. И понятно, что ошибки в планировании становились неизбежны — они сопровождали нас на всех этапах разработки. Но постепенно мы адаптировались и погрешность планирования ощутимо снизилась.

  • Декомпозиция: На первых этапах разработки было очень важно сформировать последовательность маленьких подзадач внутри больших эпиков, которая привела бы нас к заветному результату.
    Лично я начинаю испытывать дискомфорт, когда на меня сваливается действительно большая задача и нет ни малейшего понимания с чего следует начать. Нельзя просто так взять и СДЕЛАТЬ задачу. По большому счёту, любая задача — это последовательность из конечного количества простых шагов. Важно лишь встать на верный путь.

  • Постановка задач: Представим ситуацию: в пятницу у нас было планирование, команда разобралась в новой задаче и всем всё ясно. В понедельник мы выходим на работу, открываем Jira, а тикет пустой. И только грустит одинокий заголовок «Сделать "космический корабль"».
    Знакомо? И нет, хитрый план перенести планирование на утро понедельника не решает проблему. Нужно подробное описание задачи, к которому при желании можно было бы вернуться. Потраченное на оформление тикета время, скорее всего, окупится в будущем и подстрахует от подобных неожиданностей.

  • Понимание предметной области продукта и его функционала: В начале разработки лично я немало настрадался от пробелов в понимании бизнес-логики продукта. «Почему эта штука должна работать именно так, а не иначе?». Здесь могу сказать следующее: прислушивайтесь к своим коллегам, которые постигли дзен понимают в предметной области. Рано или поздно вы получите необходимый уровень знаний о продукте для комфортной работы с ним.

  • Время на рефакторинг: Если кодовая база растёт быстро, есть риск упустить тот момент, когда ваш код превращается из стройного и продуманного шедевра в нечто несуразно раздутое и запутанное. Например, появляются компоненты по 1000+ строк в каждом. Почему так получается? А потому что, мы торопимся выпустить функциональность побыстрее, а код становится совершенно нечитаемым.
    И в какой-то день всё пойдёт не так. Новые фичи будут добавляться со скрипом, исправление багов будет занимать всё больше времени. А всего этого можно избежать, если сразу заложить время на рефакторинг.

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

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

Roadmap

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

Говоря о дальнейшем развитии редактора, можно выделить следующие направления:

Направления развития

Перевод всех оставшихся аналитических панелей на новый редактор;

Добавление функциональности, не так часто используемой, но необходимой для перевода существующих аналитических панелей на новый редактор (то, что не вышло в MVP);

Добавление новых возможностей под нужды заказчиков;

Добавление новых графических компонентов под нужды заказчиков;

Развитие документации, стандартов, методологий;

Сбор обратной связи по проблемам работы редактора. Системная работа по решению проблем (в том числе автоматизация тестирования);

Проработка безопасности использования редактора.

Заключение

Как видите, нам есть к чему стремиться. Но результат был достигнут, причём хороший, если принять во внимание сроки и ресурсы. Я рад, что смог принять участие в этом проекте.
Хочу выразить благодарность моим коллегам, сказать спасибо за их профессионализм и человеческие качества. Мне было приятно работать с вами на протяжении этого года. Я уверен, что проект будет развиваться и радовать своих пользователей.
Больше спасибо, что дочитали до конца, надеюсь, что было интересно. Всем желаю успеха в работе над проектами!