Привет! Меня зовут Настя, я старший системный аналитик в X5 Tech. Я рисую sequence-диаграммы каждый день на протяжении четырёх лет. За это время я прошла все круги ада по Данте, то есть попробовала разные инструменты для рисования этих самых диаграмм. Пока не встретила его – PlantUML.
Что удивительно, инструмент довольно не новый, но тем не менее лучше него я пока не встречала. А ещё удивительно, что он не особо популярный. Когда мы запустили в управлении системного анализа первый воркшоп по PlantUML, за 3 минуты после анонса пришли 12 заявок от аналитиков разных грейдов – от Junior до Lead.
В процессе подготовки материалов к воркшопу мы искали статьи и литературу, которые помогли бы дополнительно изучить sequence-диаграммы в PlantUML. Ничего интересного мы не нашли.
На самих воркшопах участники часто говорили о том, что они пытались самостоятельно изучить PlantUML, но их пугало то, что нужно писать какой-то код и учить какой-то синтаксис. Документация достаточно обширная, но информации о том, как последовательно строить sequence почти нет.
Поэтому и появилась эта статья.
Почему PlantUML?
Первый раз я увидела PlantUML, когда работала на аутстаффе. Я была на проекте в банке, где занималась проектированием взаимодействия бэкенда и смежных систем.
Я работала на проекте второй день, когда мне дали задачу отрисовать диаграмму уже реализованного функционала по передаче данных пользователя государственному органу.
Я пошла к ПМу, чтобы узнать, как построено взаимодействие между компонентами системы. Предвкушая ваши вопросы, сразу обозначу, что ПМ на том проекте был сильно подкован технически и лучше всех в команде понимал, как работает система.
В общем, он сам нарисовал ту диаграмму, которую должна была сделать я. И делал он это в PlantUML. Я узнала, что это за инструмент и пошла его изучать. И изучала я его параллельно с тем, как рисовала диаграммы.
Почему я так быстро переключилась на PlantUML?
Среди очевидных инструментов я пробовала разные платформы для рисования. И для меня они никогда не были удобными, просто я не знала о существовании других. Перетаскивание элементов, выравнивание и соединение. У меня ещё сильно развит перфекционизм, и я всегда рисую красивые и ровные стрелки, стремлюсь к однообразию размеров шрифтов и даю пространство элементам. У меня больше времени уходило не на рисование сути диаграммы, а на её идеальный вид.
Как-то я даже рисовала sequence в Miro. Там мне понравилось больше всего из всех подобных платформ, потому что в Miro всё красивенько получается.
Потом я узнала, что PlantUML – не единственный язык, который позволяет рисовать Sequence. Например, есть ещё D2 или MermaidJS. Есть ещё штука https://sequencediagram.org/, но, как по мне, она совсем на любителя.
Я использую PlantUML по следующим причинам:
-
Не надо рисовать стрелочки.
-
Мы ведём документацию в Confluence, и там есть плагин, в котором я всё и пишу. Это удобно.
-
Ничего лишнего. В PlantUML можно сделать диаграмму, которая будет соответствовать стандартам sequence и при этом не будет ничем перегружена.
-
Неплохая документация. В ней описано всё, что можно сделать, да ещё и с примерами.
Мой алгоритм построения диаграмм
Дополнительно всё можно найти в этих ваших Интернетах.
Для примера возьму заказ кофе в кофейне. Это будет простая диаграмма, которые обычно мы не рисуем. В Sequence важно показать взаимодействие между компонентами системы или между несколькими системами. У меня задача другая — я не хочу писать, как правильно рисовать диаграммы. Я хочу показать, что PlantUML классный инструмент.
Первый шаг – плагин в Confluence. Ищете PlantUML, выбираете, добавляете заголовок и у вас на странице появляется полотно для творчества.
Второй шаг – написать @startuml и @enduml. Я не нашла сокровенной тайны, почему так надо делать (если знаете, напишите в комментариях). Я их всегда пишу в знак уважения к синтаксису, но без них диаграмма прекрасно будет отображаться.
@startuml @enduml
Третий шаг – определение участников. Обычно конструкция определения участника выглядит так:
<тип участника> <название> <опции>
Чаще всего я использую следующие типы участников:
-
actor – рисуется в виде человечка, обозначает действующее лицо.
-
participant – рисуется в виде прямоугольника, обозначает какой-то компонент системы.
-
database – рисуется в виде значка базы данных и обозначает эту самую базу данных.
-
queue – рисуется в виде значка очереди и обозначает эту самую очередь.
Название можно писать на любом языке, но есть нюанс – если в вашем названии есть пробелы, то тогда всю конструкцию надо заключить в кавычки. Или между словами добавить нижнее подчёркивание, но этот вариант мне не очень нравится.
Опции, которые я использую:
-
псевдонимы as;
-
сортировка order.
Псевдонимы – очень удобная штука. Если у вас есть длинные названия или названия на русском языке, то вы можете их использовать. Выглядит так:
<тип участника> <название> as <сокращенное_название>
Это сокращённое название нужно потом использовать в коде. Это удобно по двум причинам:
-
если название на русском, то не придётся постоянно переключать раскладку;
-
если название длинное, его можно заменить одной или несколькими буквами и сэкономить кучу времени.
Есть важная деталь – в каком порядке вы напишете участников, в том порядке они и будут отрисованы. Если вам это неудобно, то можете использовать ключевое слово order и порядок участника. Опция order идёт после опции as.
@startuml actor Покупатель as cus order 1 actor Бариста as bar order 2 participant "Кассовый аппарат" as cash order 3 database Холодильник as ref order 5 participant "Рабочее место" as work order 4 @enduml
Четвёртый шаг – добавляем в диаграмму стрелки сообщений. Тут всё просто – берём участников, добавляем между ними стрелки и называем сообщение, которым они обмениваются. Конструкция такая:
<участник><стрелка><участник>:<сообщение>
Стрелки тоже бывают разных видов, все они используются в диаграммах:
-
синхронное сообщение обозначается ->
-
асинхронное сообщение обозначается ->>
-
ответное (или возвращаемое) сообщение обозначается —>
-
собственное сообщение обозначается комбинацией участник -> участник
Ответное (или возвращаемое) сообщение можно оформить двумя способами.
Первый способ:
участник1 -> участник2
участник1 <— участник2
Второй способ:
участник1 -> участник2
участник2 —> участник1
На диаграмме они будут выглядеть одинаково, но в коде будут обозначаться по-разному. Я пользуюсь первым способом, чтобы улучшить читабельность кода.
@startuml actor Покупатель as cus order 1 actor Бариста as bar order 2 participant "Кассовый аппарат" as cash order 3 database Холодильник as ref order 5 participant "Рабочее место" as work order 4 cus -> bar: сделать заказ bar -> ref: проверить наличие продуктов bar <-- ref: продукты в наличии bar -> cash: выставить оплату cash -> cash: сформировать оплату bar <-- cash: оплата готова cus <-- bar: просьба оплатить заказ cus -> bar: оплата bar -> cash: оплата bar <-- cash: принято bar -> ref: нужно молоко bar <- ref: вот молоко bar -> work: приготовление кофе bar <-- work: кофе готов cus <-- bar: вот ваш кофе @enduml
Пятый шаг – добавляем альтернативные и опциональные сценарии. Можно ещё добавлять циклы и блоки, которые выполняются параллельно.
Все такие блоки начинаются с ключевого слова, обозначающего вид блока, и заканчиваются ключевым словом end. Конструкция такая:
<вид_блока><название_блока>
<что-то происходит>
end <вид_блока>
Первый вид блока – alt. Это альтернативный сценарий, обязательно с одной веткой else. Будет выглядеть примерно так:
alt альтернативный сценарий
участник1 -> участник2: сообщение
else другая ветка
участник1 -> участник2: сообщение
end alt
Если вам нужен просто alt без else, то можете использовать второй вид блока – opt. Это опциональный сценарий, который может выполниться при определённом условии. Выглядит так:
opt какое-то действие
участник1 -> участник2: сообщение
end opt
Третий вид блока – loop. Действия в нём повторяются несколько раз или какой-то период времени. Выглядит аналогично opt:
loop 3 раза
участник1 -> участник2: сообщение
end loop
Ещё есть четвёртый вид, который я редко использую. Это group. По сути, он группирует какое-то количество сообщений в рамочку. Это бывает нужно в тех случаях, когда у вас большая диаграмма и вам надо выделить отдельно, например, процесс оплаты. Конструкция такая же:
group какая-то группа
участник1 -> участник2: сообщение
end group
Кстати, в операторе end не обязательно писать вид блока. Можно ограничиться одним словом end. Но я люблю указывать, какой блок надо завершить, чтобы мне самой было понятно, какую группу сообщений я закрыла.
@startuml actor Покупатель as cus order 1 actor Бариста as bar order 2 participant "Кассовый аппарат" as cash order 3 database Холодильник as ref order 5 participant "Рабочее место" as work order 4 cus -> bar: сделать заказ bar -> ref: проверить наличие продуктов bar <-- ref: продукты в наличии bar -> cash: выставить оплату cash -> cash: сформировать оплату bar <-- cash: оплата готова cus <-- bar: просьба оплатить заказ loop пока не будет оплачен заказ cus -> bar: оплата bar -> cash: оплата bar <-- cash: принято end loop group приготовление кофе alt заказали кофе на альтернативном молоке bar -> ref: нужно альтернативное молоко bar <- ref: вот молоко else заказали кофе на обычном молоке bar -> ref: нужно обычное молоко bar <- ref: вот молоко end alt bar -> work: приготовление кофе bar <-- work: кофе готов end group cus <-- bar: вот ваш кофе opt покупатель оставил чаевые cus -> bar: вот чаевые cus <-- bar: большое спасибо end opt @enduml
Шестой шаг – активации. Это те самые плашечки, которые обозначают экземпляры выполнения операции. Их можно обозначать двумя способами:
-
ключевыми словами activate <участник> и deactivate <участник> – там, где вы их указываете, начинается и заканчивается активация.
-
конструкциями ++ и —
Ключевые слова activate и deactivate указываются после строки с сообщением, которую вы хотите активировать или деактивировать.
Конструкции ++ и — выглядят так:
участник1 -> участник2++: сообщение
участник2 -> участник 3—: сообщение
Как вы заметили, плюсы и минусы указываются только на втором участнике. Это важный нюанс. Нельзя указывать так:
участник1++ -> участник2++: сообщение
Для такой конструкции будет ошибка. Если вам нужно активировать участник1, то после сообщения нужно указать activate участник1 или участник1++.
Я в своих диаграммах использую только плюсы и минусы. Ключевые слова занимают много места.
@startuml actor Покупатель as cus order 1 actor Бариста as bar order 2 participant "Кассовый аппарат" as cash order 3 database Холодильник as ref order 5 participant "Рабочее место" as work order 4 cus -> bar++: сделать заказ cus++ bar -> ref++: проверить наличие продуктов bar <-- ref--: продукты в наличии bar -> cash++: выставить оплату cash -> cash: сформировать оплату bar <-- cash--: оплата готова cus <-- bar: просьба оплатить заказ loop пока не будет оплачен заказ cus -> bar: оплата bar -> cash++: оплата bar <-- cash--: принято end loop group приготовление кофе alt заказали кофе на альтернативном молоке bar -> ref++: нужно альтернативное молоко bar <- ref: вот молоко else заказали кофе на обычном молоке bar -> ref: нужно обычное молоко bar <- ref--: вот молоко end alt bar -> work++: приготовление кофе bar <-- work--: кофе готов end group cus <-- bar: вот ваш кофе opt покупатель оставил чаевые cus -> bar: вот чаевые cus <-- bar--: большое спасибо cus-- end opt
Седьмой шаг – украшения. Их очень много в PlantUML, я расскажу только про те, которыми пользуюсь я.
Первое – autonumber. Указываете это слово до всего описания сообщений, и они будут пронумерованы автоматически. В общем виде эта команда выглядит так:
autonumber <число_с_которого_начнется_нумерация><шаг>
Если вы не указываете два параметра, то автонумерация по умолчанию начинается с цифры 1 с шагом 1.
Второе – разделители. Очень удобно, когда большая диаграмма, и нужно поделить её на части. Конструкция такая:
==<заголовок>==
Третье – задержка. Нужна, чтобы обозначить, например, что сообщение нужно отправить на следующий день. Конструкция такая:
…<заголовок>…
Четвёртое – разделение сообщений. Если у вас есть большие сообщения, которые удлиняют вашу диаграмму, то их можно разделить. Тоже есть два способа:
-
автоматический – с помощью команды skinparam maxMessageSize <число>, указывается в начале диаграммы и действует на все сообщения;
-
ручной – с помощью команды \n, пишите в то место, где нужно поделить сообщение.
Пятое – промежутки. Из названия понятно, что они нужны для обозначения промежутков в диаграмме. После сообщения на следующей строке указываете ||| и будет сделан отступ. Особенно это актуально, когда много альтернативных сценариев и блоков.
@startuml actor Покупатель as cus order 1 actor Бариста as bar order 2 participant "Кассовый аппарат" as cash order 3 database Холодильник as ref order 5 participant "Рабочее место" as work order 4 autonumber cus -> bar++: сделать заказ cus++ bar -> ref++: проверить наличие продуктов bar <-- ref--: продукты в наличии ==оплата== bar -> cash++: выставить оплату cash -> cash: сформировать оплату bar <-- cash--: оплата готова cus <-- bar: просьба\nоплатить заказ ||| loop пока не будет оплачен заказ cus -> bar: оплата bar -> cash++: оплата bar <-- cash--: принято end loop ==готовка== group приготовление кофе ||| alt заказали кофе на альтернативном молоке bar -> ref++: нужно альтернативное молоко bar <- ref: вот молоко else заказали кофе на обычном молоке bar -> ref: нужно обычное молоко bar <- ref--: вот молоко end alt bar -> work++: приготовление кофе bar <-- work--: кофе готов end group ...через 3 минуты... cus <-- bar: вот ваш кофе opt покупатель оставил чаевые cus -> bar: вот чаевые cus <-- bar--: большое спасибо cus-- end opt @enduml
PlantUML и ChatGPT
ChatGPT прекрасно может работать с PlantUML. Тут работает классическое правило – хотите хороший результат, напишите детальный запрос. Для примера можем попросить просто написать код для PlantUML с sequence-диаграммой работы бариста.
Запрос: Напиши код для plantuml с sequence-диаграммой работы бариста.
@startuml actor Customer actor Barista participant Queue participant Database Customer -> Queue: Заказать кофе Queue -> Barista: Передать заказ Barista -> Database: Записать заказ Database --> Barista: Подтверждение записи Barista -> Barista: Проверить наличие ингредиентов Barista -> Database: Обновить количество ингредиентов Database --> Barista: Подтверждение обновления Barista -> Barista: Приготовить кофе Barista -> Queue: Сообщить о готовности заказа Queue --> Customer: Оповестить о готовности Customer -> Barista: Забрать заказ Barista -> Customer: Передать кофе Customer -> Barista: Оставить чаевые @enduml
Результат плохой: непонятно, что это за участники, не хватает активаций. Расположение участников тоже странное, если очередь – это физическая очередь, то зачем она вообще здесь.
Но можно попробовать сформировать более конкретно.
Запрос: Мне нужен код sequence-диаграммы для plantuml. В диаграмме должно быть 5 участников: покупатель, бариста, касса, рабочее место и холодильник. Обязательно должны быть активации и деактивации, один альтернативный сценарий с выбором молока (альтернативное или обычное) и опциональный блок для оставления чаевых. Хорошо бы добавить нумерацию сообщений.
@startuml autonumber actor Customer participant Barista participant CashRegister as "Cash Register" participant WorkStation as "Work Station" participant Fridge as "Fridge" Customer -> Cash Register: 1. Сделать заказ activate Cash Register Cash Register -> Barista: 2. Передать заказ deactivate Cash Register activate Barista Barista -> Fridge: 3. Проверить наличие ингредиентов activate Fridge Fridge --> Barista: 4. Подтверждение наличия ингредиентов deactivate Fridge alt Выбор молока Customer -> Barista: 5. Выбрать обычное молоко Barista -> Fridge: 6. Достать обычное молоко Fridge --> Barista: 7. Обычное молоко else Customer -> Barista: 5. Выбрать альтернативное молоко Barista -> Fridge: 6. Достать альтернативное молоко Fridge --> Barista: 7. Альтернативное молоко end Barista -> WorkStation: 8. Приготовить кофе activate WorkStation WorkStation -> Customer: 9. Передать кофе deactivate WorkStation deactivate Barista opt Оставить чаевые Customer -> Cash Register: 10. Оставить чаевые activate Cash Register Cash Register -> Barista: 11. Сообщить о чаевых deactivate Cash Register end @enduml
В результате мы получим ошибку, потому что ChatGPT в коде у участника Cash Register оставил пробел. Можно попросить исправить.
Запрос: Исправь ошибку с Cash Register, там не должно быть пробела.
@startuml autonumber actor Customer participant Barista participant CashRegister as "Cash Register" participant WorkStation as "Work Station" participant Fridge as "Fridge" Customer -> CashRegister: 1. Сделать заказ activate CashRegister CashRegister -> Barista: 2. Передать заказ deactivate CashRegister activate Barista Barista -> Fridge: 3. Проверить наличие ингредиентов activate Fridge Fridge --> Barista: 4. Подтверждение наличия ингредиентов deactivate Fridge alt Выбор молока Customer -> Barista: 5. Выбрать обычное молоко Barista -> Fridge: 6. Достать обычное молоко Fridge --> Barista: 7. Обычное молоко else Customer -> Barista: 5. Выбрать альтернативное молоко Barista -> Fridge: 6. Достать альтернативное молоко Fridge --> Barista: 7. Альтернативное молоко end Barista -> WorkStation: 8. Приготовить кофе activate WorkStation WorkStation -> Customer: 9. Передать кофе deactivate WorkStation deactivate Barista opt Оставить чаевые Customer -> CashRegister: 10. Оставить чаевые activate CashRegister CashRegister -> Barista: 11. Сообщить о чаевых deactivate CashRegister end @enduml
В целом даже похоже на то, что сделала я сама. Большое отличие в том, что для ChatGPT покупатель взаимодействует с кассой сам. Хотя в таком случае логичнее назвать этого участника как-то иначе.
Забавно, что ChatGPT применил команду autonumber и сам пронумеровал сообщения.
Заключение
Надеюсь, что эта статья помогла вам в освоении PlantUML. И в понимании того, что PlantUML – лёгкий инструмент.
Мне кажется, что для первых диаграмм достаточно остановиться на участниках, сообщениях и активациях. И уже по мере освоения добавлять альтернативные сценарии и блоки, а потом и разные украшения.
Если хотите узнать больше возможностей инструмента, то рекомендую использовать документацию: https://plantuml.com/ru/sequence-diagram. И ещё у них есть неплохой редактор, который сразу будет вам показывать всё то, что вы напишете: http://www.plantuml.com/plantuml/uml/.
ссылка на оригинал статьи https://habr.com/ru/articles/821687/
Добавить комментарий