В последние пару лет стал больше заниматься документацией. Написать поясняющий текст о том, как работает та или иная система — в целом, это достаточно просто. Нарисовать схему, на которой будут отображены все ключевые объекты, связи между этими объектами, тоже вполне легко.
Но самый проблемный момент — это поддерживать эту документацию в актуальном состоянии. И ладно бы текст, но схемы… Т.к. вся документация онлайн, т.е. в формате html, то к тексту прилагаются картинки gif/jpeg/png, на которых собственно изображены схемы. А схемы рисуются в различных программах типа Visio или онлайн-сервисах а-ля draw.io. Затем экспортируешь схему в графический формат и прилагаешь к html. Все просто.
В чем проблема?
Схемы обычно простые. Точнее, не сильно сложные. Да, количество объектов десяток-два, количество связей примерно столько же. Плюс подписи, какие-то обозначения. Простые схемы и на словах описать можно, а слишком сложные, кх-м… (с) «не поймут-с». Схем много, изменения в них нужно вносить периодически-эпизодически, т.е. постоянно, т.к. они идут вслед за разработкой наших продуктов.
Можно же встраивать html сервиса. Пробовал?
Да, конечно. Мне, например, нравятся графики gliffy.com. Но для изменений надо идти в сторонний сервис, там править. И сложнее делегировать сделать поправки коллеге.
Что делать?
Недавно на гитхабе мне попался в рекомендациях репозиторий . Диаграмма как код. Т.е. мы описываем на js нужную нам схему. Этот js мы пишем прямо в том же html, где и прочий текст документации.
К слову сказать, но я пишу документацию не совсем в html. Обычно документация — это набор файлов с markdown-текстом, который затем конвертируется в полноценный сайт документации каким-нибудь движком, например wintersmith. Или wiki-система.
Получается очень удобно: вот мы написали текст, затем открывается тег script и в нем описан js код схемы.
Что опять не так?
Этот репозиторий мне понравился, но это не единственный пример когда диаграмму рисуют с помощью кода или текстового представления. (В конце статьи будут ссылки проектов и статей, которые нагуглил по теме diagram as code.)
И ведь я не один правлю документацию. Иногда свою лепту вносят и коллеги — слово поправить, описание изменить, новые картинки вставить.
Поэтому хотелось бы диаграмму видеть в читаемом понятном текстовом формате, которому бы не пришлось долго обучаться. А местами даже просто copy-paste сделать для ускорения добавления новой схемы.
А еще один коллега заметил, что код это, конечно, хорошо, но если использовать структуру, все может быть очень строго и выразительно.
Поэтому я попробовал представить схему как набор нескольких небольших массивов, которые описывают узлы, связи, группы узлов, а также расположение узлов. Получилось на мой скромный взгляд достаточно удобно, хотя, конечно, на вкус и цвет…
Как это диаграмма в массиве?
- Каждый узел описывается идентификатором, который однозначно определяет узел.
- Также к узлу можно добавить иконку, добавить надпись.
- Между двумя узлами можно указать связь.
- Для связи на схеме можно задать цвет, надпись.
- Направление связи определяется как от источника к цели. А источник и цель указываются идентификаторами узла.
- Один и более узлов можно добавить в группу.
- Связь также можно указать и от группы, и к группе.
Пользуясь этими простыми правилами получается вот такая схема. Просто? Вполне.

А описывается она следующим js-кодом. Основное здесь — это объект elements. В котором указаны nodes — узлы, edges — связи.
const elements = {
nodes: [ // описываем узлы
{ id: 'client', type: 'smartphone', label: 'Mobile App'},
{ id: 'server', type: 'server', label: 'Main Server'},
{ id: 'db1', type: 'database', label: 'DB 1'},
{ id: 'db2', type: 'database', label: 'DB 2'},
],
edges: [ // указываем связи
{ source: 'client', target: 'server', label: 'request' },
{ source: 'server', target: 'db1', label: 'request' },
{ source: 'server', target: 'db2', label: 'request' },
],
};
Diagram('scheme1', elements);
Конечно, отрисовку схемы я придумал не сам, а воспользовался библиотекой — очень мощный инструмент визуализации. Толику возможностей которой в своем решении использую лишь.
Понятно, это простой пример. Можно посложнее?
Да, пожалуйста. Для указания позиций — мы используем positions, для указания групп — указываем список групп в groups, а у самих элементов атрибут group.

А это код:
<div id="scheme5" style="height:500px;width:800px;"></div>
<script>
const elements5 = {
groups: [
{ id: 'g1', label: 'Группа сервисов 1'},
{ id: 'g2', label: 'Группа сервисов 2'},
],
nodes: [
{ id: 'man1', type: 'person', label: 'Человек'},
{ id: 'client', type: 'smartphone', label: 'Смартфон'},
{ id: 'agent-backend', type: 'server', group: 'g1', label: 'agent-backend'},
{ id: 'web', type: 'server', group: 'g1', label: 'Приложение admin'},
{ id: 'www', type: 'server', group: 'g1', label: 'страница загрузки'},
{ id: 'mongodb1', type: 'database', group: 'g1', label: 'Mongo DB 1'},
{ id: 'mongodb2', type: 'database', group: 'g1', label: 'Mongo DB 2'},
{ id: 'runner-integration1', type: 'worker', group: 'g1', label: 'отправка'},
{ id: 'runner-integration2', type: 'worker', group: 'g1', label: 'отправка'},
{ id: 'api', type: 'server', group: 'g1', label: 'API'},
{ id: 'server2', type: 'server', group:'g2', label: 'сервер'},
{ id: 'otherServer', type: 'server', group:'g2', label: 'сервер'},
{ id: 'firebase', type: 'cloud', label: 'Google Firebase'},
],
edges: [
{ source: 'client', target: 'agent-backend', label: 'json', color: 'red' },
{ source: 'agent-backend', target: 'mongodb1', color: 'red' },
{ source: 'agent-backend', target: 'mongodb2', color: 'red' },
{ source: 'mongodb1', target: 'runner-integration1', label: 'данные' },
{ source: 'mongodb2', target: 'runner-integration2', label: 'данные' },
{ source: 'mongodb1', target: 'web', label: 'данные для отображения' },
{ source: 'runner-integration1', target: 'server2', label: 'данные' },
{ source: 'runner-integration2', target: 'otherServer', label: 'данные' },
{ source: 'api', target: 'firebase', label: 'запросы', color: 'blue', },
{ source: 'firebase', target: 'client', label: 'push', color: 'blue'},
{ source: 'server2', target: 'api', label: 'уведомления', color: 'blue'},
{ source: 'man1', target: 'client', },
],
positions: [
{ id: 'client', row: 2, col: 1,},
{ id: 'agent-backend', row: 2, col: 3,},
{ id: 'web', row: 6, col: 3,},
{ id: 'www', row: 1, col: 3,},
{ id: 'mongodb1', row: 1, col: 4,},
{ id: 'mongodb2', row: 2, col: 5,},
{ id: 'runner-integration1', row: 3, col: 3,},
{ id: 'runner-integration2', row: 4, col: 3,},
{ id: 'api', row: 5, col: 3,},
{ id: 'server2', row: 6, col: 7,},
{ id: 'otherServer', row: 4, col: 7,},
{ id: 'firebase', row: 5, col: 1,},
{ id: 'logger', row: 2, col: 7,},
{ id: 'crm', row: 5, col: 8,},
],
};
Diagram('scheme5', elements5, {layout: 'grid'});
</script>
Такая схема с одной стороны — это почти пара экранов кода на ноуте, с другой структура а-ля json позволяет заполнять все данные по аналогии, быстро и можно copy-paste.
А почему positions вынесены отдельно от узлов?
Так удобнее. Сначала мы указываем nodes. Затем можем указать пару-тройку групп и указать их в узлах. Затем обозначаем связи. А уж затем, когда основные объекты и связи между ними есть, беремся за расположение этих объектов на схеме. Или наоборот.
А можно без positions?
Можно и без positions. Но это будет немного скомкано, в примерах можно посмотреть такой вариант. Это обусловлено тем, что для cytoscape есть алгоритм расположения узлов , который также учитывает наличие групп. Указание positions делает схему более контролируемой, но на стадии первого наброска схемы можно и без positions.
Также positions можно указывать в стиле Морского боя. Т.е. один узел располагается в a1, а другой в d5. Особенно помогает, что cytoscape формирует объекты на canvas подвижными, т.е. мы можем их подвигать, посмотреть разные варианты расположения, а затем зафиксировать в коде понравившееся расположение элементов.
В целом, понятно. Можно попробовать?
Конечно, для быстрого создания схем сделал себе небольшой , который сам обновляет схему и в браузере хранит последний вариант (в localStorage).
Попробовали? Можно теперь и к себе на страницу добавить.
Тогда еще раз:
1. Подключаем скрипт
<script src="https://unpkg.com/@antirek/network-diagram@0.1.4/dist/code-full.min.js"></script>
2. Добавляем в html код
<div id="scheme1" style="height:300px;width:800px;"></div>
<script>
const elements = {
nodes: [
{ id: 'client', type: 'smartphone', label: 'Mobile App'},
{ id: 'server', type: 'server', label: 'Main Server'},
{ id: 'db1', type: 'database', label: 'DB 1'},
{ id: 'db2', type: 'database', label: 'DB 2'},
],
edges: [
{ source: 'client', target: 'server', label: 'request' },
{ source: 'server', target: 'db1', label: 'request' },
{ source: 'server', target: 'db2', label: 'request' },
],
};
Diagram('scheme1', elements);
</script>
3. правим код до нужной нам схемы (думаю, это проще чем нарисовать сову 🙂
Еще подробнее на на гитхабе.
Что в итоге?
Своих целей я достиг — сделать добавление схем inline в документацию, формат достаточно простой и понятный. Для суперсхем не подойдет, а для небольших схем, поясняющих структуру связей — очень даже ничего. Всегда можно быстро подправить и что-то с течением времени поменять. Да, и коллеги могут в доке сами что-то подправить, как минимум подписи к объектам без особого обучения ))
Что можно улучшить?
Тут вариантов, конечно, масса. Сделать добавление дополнительных иконок (все имеющиеся добавлены inline в скрипт). Подобрать более выразительный набор иконок. Сделать возможность указания стиля линии связей. Добавить фоновое изображение.
А что думаете вы?
У меня уже есть несколько идей на реализацию в issues, вы также добавьте свои в комментарии.
Мое решение определенно применимо в узком спектре задач, и возможно вы найдете более удобный инструмент для рисования диаграмм, просто закодировав их — как говорится ‘show me your diagram as code’
- (9 типов графиков онлайн-редактор)
- И если вам любы супер детальные и сложные схемы — то вас определенно восхитит этот проект:
Источник: habr.com
