Make Data Visualization Great Again (или как мы создавали свой инструмент визуализации данных)
- среда, 2 апреля 2025 г. в 00:00:06
Всем привет! Меня зовут Олег Косарев. Я – ведущий инженер-программист. В группе компаний «Цифра» занимаюсь развитием и поддержкой продукта «Диспетчер». Представляю команду «ОСР» (общесистемное развитие). Моя основная специализация – Frontend-разработка.
В данной статье я хочу поделиться с вами нашим опытом по созданию MVP (Minimum Viable Product, «минимально жизнеспособный продукт») редактора аналитических панелей.
Дисклеймер: Данная статья — своего рода «road movie», рассказывающий о нашей работе за прошедший год. Я старался обойтись без излишних технических подробностей, чтобы сделать материал максимально доступным и понятным. Статья будет полезна Frontend-разработчикам, которые увлечены своей профессией, а также тем, кто работает с BI-инструментами. Кроме того, я надеюсь, что она сможет помочь тем, кто выгорел и находится в поисках вдохновения. Ведь (спойлер) это история о преодолении трудностей и достижении целей.
Для начала хотелось бы сказать пару слов о комплексе «Диспетчер». Это программное решение, предназначенное для контроля производственных процессов и мониторинга оборудования на промышленных предприятиях. Система построена на автоматическом сборе объективных данных (IoT) и современных технологиях Индустрии 4.0.
Аналитические панели (дашборды) в системе «Диспетчер» — это интерактивные визуализации данных, которые помогают операторам, диспетчерам и руководителям в режиме реального времени контролировать ключевые показатели работы оборудования и персонала. Они предоставляют удобный интерфейс для анализа информации, принятия решений и оперативного реагирования на изменения.
Для реализации проекта была сформирована небольшая команда: продакт-менеджер, тимлид и два разработчика. На всё про всё нам потребовалось 20 спринтов или 10 месяцев в пересчёте на реальное время: от момента первого коммита до момента внедрения редактора в основное решение «Диспетчер.MDC» и замены части существующих (стандартных) дашбордов на наши собственные.
А теперь по порядку, как всё начиналось. В нашем продукте уже «жили» дашборды, построенные с помощью DevExpress Business Intelligence Dashboard. Возникает справедливый вопрос: для чего нам потребовался свой собственный инструмент визуализации данных? Проще всего это объяснить сравнением в таблице.
DevExpress Dashboard | Наше решение | Польза для заказчика |
Ограниченные возможности для развития текущего редактора аналитических панелей, поскольку это решение — стороннее. | Возможность добавления любых компонентов и функциональности. | Возможность добавлять любые необходимые заказчикам компоненты и функциональность, которые невозможно реализовать в текущем редакторе. |
Создание аналитических панелей — достаточно сложный и трудоемкий процесс (текущий редактор создан для использования в различных сферах). | Упрощенный интерфейс. Отказ от компонентов и функциональности, которые не использовались нами в редакторе DevExpress (новый редактор «заточен» исключительно под наши нужды). | Снижение трудоемкости создания аналитических панелей под нужды заказчиков. Ускорение оказания технической поддержки. |
Отличие компонентов и интерфейса продуктов комплекса «Диспетчер» и редактора аналитических панелей. | Унификация компонентов, интерфейса редактора аналитических панелей и продуктов комплекса «Диспетчер». | Упрощение взаимодействия с системой в целом. Единый дизайн компонентов и интерфейса. |
Хорошо, допустим, что разработка собственного BI-инструмента для нас вполне оправдана. Но как подступиться к его реализации? Начали по старинке — с анализа конкурента. Мы изучили, как был организован DevExpress Dashboard и выделили следующие его составные части:
Макет (*);
Панель компонентов (*);
Конструктор запросов (*);
Контекстное меню компонента (настройки);
Редактор формул;
Настройки цветовой схемы.
Здесь (*) отмечены, те составляющие, которые мы посчитали обязательными для этапа PoC (Proof of Concept — процесс проверки и демонстрации жизнеспособности идеи или технологии до её полной реализации).
После декомпозиции, нарезки эпиков на отдельно взятые задачи и ОЧЕНЬ приблизительной оценки в часах по каждому разделу мы приступили к разработке.
Тут хочу сделать небольшое лирическое отступление. В профессиональной жизни разработчик редко начинает проект с нуля (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-разработчика стояли следующие подзадачи:
Необходимо реализовать представление конструктора в качестве диаграммы (графа);
Обеспечить возможность добавлять таблицы БД в качестве узлов;
Обеспечить возможность создавать связи между узлами (таблицами);
Обеспечить возможность взаимодействия с конкретными узлами (задание настроек самой таблицы, задание настроек полей таблицы);
Обеспечить возможность взаимодействия со связями между узлами (опять же настройки);
Конвертировать получившийся в результате граф в объект, который будет отправлен на сервер для вычисления итогового запроса.
Большинство проблем нам помог решить React Flow. Это библиотека React-компонентов для создания и управления графическими диаграммами (графами). Она предоставляет удобные инструменты для визуализации сложных структур, позволяя пользователям взаимодействовать с элементами графа, перетаскивая узлы, соединяя их связями и управляя их состоянием.
Таким образом, сразу же была закрыта основная часть проблем реализации конструктора запросов. Мы убедились на практике, что React Flow предоставляет удобный API для настройки узлов и сбора данных по ним. С таким инструментом, настройка таблиц и полей с последующей конвертацией всего графа в JSON уже не представлялось сложной задачей.
Вот здесь доступно небольшое демо: Codesandbox.
Имея готовый конструктор, мы уже могли собрать простейший дашборд. Этап PoC подошёл к концу. Всё это заняло порядка двух месяцев (неплохой промежуточный результат). После этого мы смогли полностью сосредоточиться на «наращивании мышц» нашего проекта: работать над добавлением новых компонентов для макета, углубиться в настройки отображения элементов дашборда.
Забегая вперёд, скажу, что всего нами было реализовано 16 различных компонентов дашборда и НЕСЧЁТНОЕ количество настроек для их отображения. Останавливаться на них отдельно не будем, просто посмотрим что у нас получилось:
Кратко пробежавшись по проделанной работе, хотелось бы вернуться к проблемам, с которыми мы столкнулись во время разработки. Но сначала вопрос: часто ли вы возвращаетесь к заметкам сделанным во время ретроспектив? И делаете ли такие заметки в принципе? Расскажите в комментариях…
Мне удалось поднять историю наших ретро со второго по шестнадцатый спринт. Похоже, мы не сразу начали уделять время анализу проделанной работы. И вот на какие проблемы я хотел бы обратить внимание:
Планирование: Планирование зачастую является уязвимой точкой в процессе разработки. Чтобы оценить свои силы, а уж тем более в часах, нужен опыт. В начале такого специфического в предметной области опыта у нас, разумеется, не было. Он приходил со временем в процессе работы. И понятно, что ошибки в планировании становились неизбежны — они сопровождали нас на всех этапах разработки. Но постепенно мы адаптировались и погрешность планирования ощутимо снизилась.
Декомпозиция: На первых этапах разработки было очень важно сформировать последовательность маленьких подзадач внутри больших эпиков, которая привела бы нас к заветному результату.
Лично я начинаю испытывать дискомфорт, когда на меня сваливается действительно большая задача и нет ни малейшего понимания с чего следует начать. Нельзя просто так взять и СДЕЛАТЬ задачу. По большому счёту, любая задача — это последовательность из конечного количества простых шагов. Важно лишь встать на верный путь.
Постановка задач: Представим ситуацию: в пятницу у нас было планирование, команда разобралась в новой задаче и всем всё ясно. В понедельник мы выходим на работу, открываем Jira, а тикет пустой. И только грустит одинокий заголовок «Сделать "космический корабль"».
Знакомо? И нет, хитрый план перенести планирование на утро понедельника не решает проблему. Нужно подробное описание задачи, к которому при желании можно было бы вернуться. Потраченное на оформление тикета время, скорее всего, окупится в будущем и подстрахует от подобных неожиданностей.
Понимание предметной области продукта и его функционала: В начале разработки лично я немало настрадался от пробелов в понимании бизнес-логики продукта. «Почему эта штука должна работать именно так, а не иначе?». Здесь могу сказать следующее: прислушивайтесь к своим коллегам, которые постигли дзен понимают в предметной области. Рано или поздно вы получите необходимый уровень знаний о продукте для комфортной работы с ним.
Время на рефакторинг: Если кодовая база растёт быстро, есть риск упустить тот момент, когда ваш код превращается из стройного и продуманного шедевра в нечто несуразно раздутое и запутанное. Например, появляются компоненты по 1000+ строк в каждом. Почему так получается? А потому что, мы торопимся выпустить функциональность побыстрее, а код становится совершенно нечитаемым.
И в какой-то день всё пойдёт не так. Новые фичи будут добавляться со скрипом, исправление багов будет занимать всё больше времени. А всего этого можно избежать, если сразу заложить время на рефакторинг.
В действительности, большинство из рассмотренных выше проблем мы в той или иной степени побороли к моменту завершения этапа PoC. Так что, мы однозначно стали лучше как команда, и уровень экспертизы каждого заметно вырос.
Что касается ресурсов, если вы начнете похожий проект, не забудьте еще добавить в команду системного аналитика и тестировщика, он точно необходим как воздух.
Подведём итоги. У нас получилось реализовать свой собственный редактор аналитических панелей. Уже сейчас наши аналитики активно пользуются этим инструментом, и мы собираем обратную связь для того, чтобы сделать наш продукт ещё лучше. Уже с новым релизом наши клиенты так же смогут начать работать с обновлёнными дашбордами.
Говоря о дальнейшем развитии редактора, можно выделить следующие направления:
Направления развития |
Перевод всех оставшихся аналитических панелей на новый редактор; |
Добавление функциональности, не так часто используемой, но необходимой для перевода существующих аналитических панелей на новый редактор (то, что не вышло в MVP); |
Добавление новых возможностей под нужды заказчиков; |
Добавление новых графических компонентов под нужды заказчиков; |
Развитие документации, стандартов, методологий; |
Сбор обратной связи по проблемам работы редактора. Системная работа по решению проблем (в том числе автоматизация тестирования); |
Проработка безопасности использования редактора. |
Как видите, нам есть к чему стремиться. Но результат был достигнут, причём хороший, если принять во внимание сроки и ресурсы. Я рад, что смог принять участие в этом проекте.
Хочу выразить благодарность моим коллегам, сказать спасибо за их профессионализм и человеческие качества. Мне было приятно работать с вами на протяжении этого года. Я уверен, что проект будет развиваться и радовать своих пользователей.
Больше спасибо, что дочитали до конца, надеюсь, что было интересно. Всем желаю успеха в работе над проектами!