что такое workflow в разработке
Автоматизация workflow небольшой команды разработки (Часть 1)
Практически во всех местах моей работы программистом для разработки использовали всего два продукта: багтрекинг и систему контроля версий. Чаще всего это были Atlassian Jira и SVN. В принципе, наличие этих двух систем здорово упорядочивает общение всех участников процесса разработки и положительно влияет на качество работы отдела и продукта.
Итак. Настройка ПО, сопровождающего процесс разработки
Crowd и Jira
Первым был Crowd — менеджер учетных записей. Я скрестил и синхронизировал его с Jira. Crowd втянул в себя все группы и всех пользователей Jira. В Jira работу с директорией Crowd я сделал read/write (поскольку нового пользователя добавлять через Jira удобнее чем через Crowd).
В Jira всех пользователей разбил на группы:
Если вкратце, то юзеры в группе могут только создавать задачки и наблюдать за ними, девелоперы могут их редактировать и решать, а админы управляют версиями, компонентами, могут удалять или редактировать чужие комментарии или задачи.
Confluence
Я считаю этот продукт самым удачным среди всевозможных баз знаний. После прочтения большого количества статей и сравнения разных систем, я пришел к выводу, что Confluence среди них бесспорный лидер.
В нем я создал разные Пространства:
Весь хелп у нас хранится в Confluence в виде иерархической структуры статей. В конце версии одним кликом получается пачка HTML файлов, которые ссылаются друг на друга. Все это удовольствие мы просто копируем в проект и выпускаем. И потому справка у нас постраничная (а не все в одном огромном файле), легко поддерживаемая и всегда онлайн доступна для всей команды (а не где-то у кого-то в какой-то папке).
Каждый проект Jira и пространство Confluence связаны. В статье с постановкой есть ссылка на задачу и наоборот.
Bitbucket
Для хранения исходников мы исторически использовали SVN. Влияние новых технологий не прошло мимо, и конечно же выбор пал на git (бест практик как никак).
Поскольку я программист, а не сисадмин, установку чистого гита я не осилил. Потому взял готовый пакет GitBlit… но вскоре в нём разочаровался. В результате все перевёл на GitLab.
Год работы и 12 одновременно действующих проектов дали о себе знать. Сервер стал прогибаться под тяжестью руби. К тому же сказалась очень слабая совместимость с Atlassian продуктами.
В то время я заметил Stash. В чистом виде под линукс я, к сожалению, его не нашёл, за то поставил его в составе Bitbucket. И понеслась!
Создал проекты и по репозиторию в каждом. Дал полные права каждому ПМ-у на свой проект (теперь он сам может создавать репозитории в своём проекте сколько хочет). В репозитории на мастер ветку выдал права только лиду и по умолчанию выставил ветку dev.
Скрестил Bitbucket с Jira и теперь в каждой задаче есть список всех комитов по задаче. А из комитов можно переходить на задачки.
Fisheye and Crucible
Где-то я читал, что Crucible можно встроить в Stash. Но поизучав детально уже настроенный Bitbucket, я ничего подобного не нашёл. А так как CodeReview — обязательный этап в нашем Workflow, то пришлось ставить и Fisheye. Реально очень удобная штука, но имея Bitbucket, можно было обойтись и без неё.
Когда у нас стоял GitLab, добавлять репозитории в Fisheye было реально морочно… Куча настроек, генерация ключей, левые юзеры… С Bitbucket все пошло как по маслу. Скрестил Fisheye и Bitbucket и в Fisheye появился список всех репозиториев с кнопочкой “add”.
Создал проекты, указал в них группы разработки те, что в Jira для этих проектов, указал репозиторий и скрестил каждый со своим проектом в Jira. А в Jira наоборот указал в каждом проекте линки на Fisheye и Crucible и путь к репозиторию.
Теперь в каждой задаче есть комиты по задаче не только из Bitbucket, но и из Fisheye. Безтолково, конечно….но ничего страшного. Зато в каждой задаче теперь можно сразу увидеть Review и его статус!
Jenkins
Вот вроде бы и все, но нет! Надо же это все хозяйство как то автоматизированно собирать. Очень долго хотел прикрутить Bamboo, но он выглядит ущербно по сравнению с Jenkins. В Jenkins мне удавалось настроить автосборку всего еще и с перламутровыми пуговицами. В Bamboo все не так. Сообщество слабое, плагинов мало, можно рулить только выполнением консольных команд. Перечислять недостатки не буду. В начале настраивал под сборку Delphi-проектов, но потом перешли на веб и сборки стали простыми — вытащил, залил на ftp, отметил в Jira и письма разослал. Подумаю, может и на Bamboo перейдем.
Значит скрещивать Jenkins ни с кем не стал. Вроде как нужды нет. Никому не интересно в задаче сколько раз она собиралась. Единственное что — это в самом Jenkins поставил Jira plugin, чтобы при сборке задачи автоматически перемещались на другой шаг WorkFlow.
И важно все вышеперечисленные продукты обязательно настроить на Crowd. Единые учетки во всех этих системах — это реально удобно. При найме нового члена команды, достаточно его просто занести в Jira и указать его группу-проект и он имеет доступ ко всему. Точно так же и при увольнении. В одном месте выключил и доступа нет никуда.
Уголок сисадмина
Сервер у нас стоит Xeon X3430 4CPUs x 2,4 Ghz, 8 GB, 1TB
Вначале, я поднял Windows Server и на нем все эти продукты сразу + Ubuntu для GitLab. Сервер не осилил, раз в два часа просто зависал на 10 мин.
После этого принял решение разделить по разным виртуалкам. Вот что получилось:
Gateway — для выхода в Интернет (4 ядра, 4ГБ ОЗУ) — эта виртуалка мне уже досталась от админа, который этот сервер первоначально настраивал.
Jira — 2 ядра, 2ГБ ОЗУ
Confluence — 2 ядра, 2ГБ ОЗУ
Bitbucket&Jenkins — 2 ядра, 2ГБ ОЗУ
Crowd&FishEye&FTP — 2 ядра, 2ГБ ОЗУ
Все виртуалки на Linux Debian 8.2
Теперь все эти продукты летают, и зависаний, как раньше, нет.
На виртуалке Gateway пробросил два порта чтобы снаружи были доступны Jira и Confluence. Размышляю над тем, чтобы еще пробросить порт для доступа к Git. Но чтобы было более секурно, только ssh доступ.
Итоги
Вот и готовы сервера и все необходимые продукты для полной автоматизации workflow разработки. В следующей части я постараюсь подробно описать как взаимодействуют эти все продукты ежедневно в процессе разработки.
Современная workflow-система. Возможности, инструменты, Value Stream
Компании несут убытки из-за неэффективности рабочих процессов. До появления необходимого ПО люди организовывали, контролировали и стандартизировали этапы работы вручную. Даже сегодня компании часто ведут документацию и отчетность на бумажных носителях. Однако новые бизнес-технологии помогают ускорить процессы и повысить их эффективность. Одним из этапов цифровой трансформации компании может стать внедрение системы Workflow. Далее расскажем про современные модификации таких систем, а также поговорим о перспективах развития технологии в рамках ITIL 4.
Это технология управления и автоматизации, при которой потоки работ организованы в последовательность шагов в соответствии с набором правил. Она направлена на организацию повседневных задач персонала. Система Workflow координирует рутинные процедуры, упрощает и ускоряет их выполнение. При этом фокус направлен на роль конкретных людей и автоматизацию их действий на каждом этапе бизнес-процесса. Workflow — удобный инструмент для координации работы отделов, когда нужно четко определить, кто, что и когда должен сделать.
Концепция появилась в 1990-х параллельно с развитием идеи об управлении бизнес-процессами (Business Process Management, BPM). Однако не стоит их путать. BPM — комплексный подход, который концентрируется на стратегических задачах, в то время как Workflow направлена на решение задач тактических. Управление бизнес-процессами работает над всей цепочкой взаимодействия с клиентом, а не только над отдельными этапами и видами процедур. Поэтому внедрение BPM требует более сложных изменений, чем Workflow.
Возможности workflow-систем
По мере того как компании становятся более ориентированными на клиентов, цифровая трансформация бизнес-процессов становится необходимостью. Независимо от сложности каждая система Workflow должна поддерживать 3 главных типа функций.
Создание и модификация workflow-процессов. В системе должны быть конкретные инструменты для их моделирования и изменения. Также важно реализовывать принцип If This Then That («если это, тогда то»), чтобы понимать, при каких условиях предпринимаются следующие шаги.
Реализация workflow-процессов. К этой функции относится маршрутизация документов (их перемещение исполнителю и сбор информации об их статусе); управление задачами (их создание и назначение ответственному лицу); управление состояниями (контроль за изменениями, которые вызвал процесс) и уведомление о событиях.
Мониторинг workflow-процессов. Система должна быть прозрачной, чтобы пользователи могли отслеживать состояние запущенных процессов и вносить в них изменения. Также в ней должны быть инструменты для формирования единых отчетов.
В дополнение к этим основным функциям, системы Workflow могут взаимодействовать с популярными пакетами офисных приложений, интегрироваться с системами управления контентом и другим корпоративным ПО. Также они должны обеспечивать конфиденциальность данных. Комплексные системы Workflow помогают организациям соблюдать отраслевые и правительственные правила, например о хранении информации или водяных знаках.
Low Code и No Code Workflow
По мере того как организации переживают цифровую трансформацию, их главным приоритетом становится модернизация процессов. Пользователям без опыта в разработке нужно быть гибкими, чтобы настраивать и модифицировать процессы для удовлетворения быстро меняющихся потребностей рынка. Речь идет не столько о работе со сложными схемами и диаграммами рабочих процессов, сколько об ускорении небольших, но трудоемких повседневных задач. Повсеместная диджитализация привела к появлению систем Low Code Workflow. В них заложены готовые сценарии и шаблоны, которые адаптированы к каждому типу рабочих процессов. Это требует от сотрудников минимальных технических навыков. Еще более прогрессивная концепция — системы No Code. Их разработали для того, чтобы пользователи без знаний в ИТ автоматизировали процессы и повышали эффективность за счет экономии на масштабе. Системы Low Code и No Code позволяют своевременно реагировать на постоянно меняющуюся рыночную среду, быстро создавая и корректируя рабочие процессы.
Система Workflow на практике
Workflow-процессы состоят из ряда последовательных действий, таких как создание новых записей, уведомление пользователей или выполнение определенных сценариев. Каждая система Workflow — это интерфейс для создания и изменения рабочих процессов путем добавления и соединения между собой отдельных операций. Это своеобразный графический редактор, который изображает систему workflow-процессов в виде блок-схемы. Операции представляют собой поля с дополнительной информацией, а переходы от одного действия к другому обозначены линиями. У пользователей системы может быть разный уровень доступа. Одни могут только создавать новые workflow-процессы, у других есть также возможность изменять и удалять их.
Workflow-система позволяет настроить такие параметры, как область применения каждого процесса, условия его запуска, расписание, входные данные и временные метрики. Также она содержит данные об авторе workflow-процесса и историю действий по каждому из них.
Современные workflow-системы: новый этап эволюции управления услугами
ITIL 4, последняя версия библиотеки лучших практик управления ИТ-услугами, была выпущена в 2019 году. Один из основных компонентов ее фреймворка — это сервисная система создания ценности (Service Value System, SVS). Ее основная идея заключается в том, что все процессы в организации направлены на достижение одной главной цели: предоставить ценный продукт конечному потребителю. Основа SVS — это цепочка создания ценности (Service Value Chain, SVC). Эта операционная модель объединяет различные виды деятельности для предоставления услуг. Их можно комбинировать разными способами, что позволяет создавать гибкие потоки создания ценности (Value Stream, VS). Правильно настроенная workflow-система может в этом помочь.
Для того чтобы конечный потребитель получил максимально качественную услугу или продукт, каждому участнику процесса необходимо полностью видеть свою роль и обязанности в потоке создании ценности. Это помогает сотрудникам оперативно решать свои задачи и вносить свой вклад в создание ценности.
Большинство workflow-систем построены по схожему принципу: пользователи могут сформировать процесс, связанный, например, только с инцидентом или же только с проблемой. Несмотря на это, сквозной поток создания ценности можно реализовать с помощью бизнес-правил и запуска Workflow по связанным сущностям. При этом участники процесса благодаря связям ITSM-объектов могут ориентироваться в сквозном потоке и осознавать распределение обязанностей.
Преимущества использования систем Workflow
Согласно исследованию McKinsey, в 2020 году автоматизацией занялось две трети опрошенных компаний по сравнению с 57% двумя годами ранее. Организации, которые включают внедрение workflow-системы в список своих приоритетов, видят следующие позитивные изменения.
Для достижения всех этих целей и удовлетворения других потребностей организаций разработано множество различных систем Workflow (Low Code, No Code). Область управления услугами постоянно меняется и развивается, и сегодня перед workflow-системами стоит новая задача — стать частью потока создания ценности.
Введение в Microsoft Workflow Foundation
Заинтересовались — читайте дальше.
Введение
Если оглянуться, то мир вокруг нас — это бесконечная череда сменяющих друг друга процессов. Мы сажаем зерно, оно прорастает, цветет, оставляет потомство, погибает. На его месте вырастает что-то новое. И так день за днем. Люди пытаются описать эти процессы, симулировать их на компьютере, придумывают что-то новое. Для этого создаются различные ментальные модели, которые упрощают описание повседневных процессов. Вводятся уровни абстракции. Например, придумали объектно-ориентированное программирование. С его помощью можно описывать объекты реального мира. Это легко и естественно. Достаточно посмотреть на вещь и в голове уже вырисовывается более или менее точная модель: какими свойствами обладает этот объект, какие действия может совершать.
Но объекты, которые никак не взаимодействуют между собой, не имеют никакого смысла. Жизнь — это движение, зачастую цикличное. С другой стороны в мире существует человек. Он вносит некоторую сумятицу своей разумностью. Многие вещи, которые он делает — непоследовательны. Мотивы не всегда ясны. Сроки не всегда соответствуют ожиданиям. Человек может о чем-то забыть, что-то сделать не так, как задумывал раньше. И в тоже время он тоже оперирует объектами. Будь то объекты реального мира, такие, как камни, цветы, вода, или виртуального: документы, информация, слова.
Именно для описания движения и взаимодействия объектов внутри программ создана технология Microsoft Workflow Foundation. Это связующее звено, которое позволяет создавать взаимодействия, связывающие объекты между собой или процессы, протекающие внутри самих объектов. Workflow Foundation (WF) разделяет все процессы на два основных типа: последовательные процессы (sequential) и процессы, основанные на состояниях (state machine).
Последовательные процессы
Последовательные процессы, в понимании WF, — это такие процессы, которые обычно происходят без вмешательства извне. Также, они занимают относительно немного времени. Хорошим примером такого процесса может послужить копирование файлов из одной папки в другую: мы задали папки в начале процесса, а потом в него не вмешиваемся до завершения. Т.е. это, по сути, отлаженный конвейер. Да, может что-то сломаться и потребовать каких-то действий, но это исключительные ситуации, которые обрабатываются отдельно.
Процессы, основанные на состояниях
Лучший пример процесса, основанного на состояниях, кроется в его переводе. State machine буквально означает государственный аппарат. Это такой процесс, который имеет множество состояний, которые в зависимости от различных событий могут переходить из одного в другое. Все это мы обычно видим в бюрократическом аппарате: государственная страховка, продажа квартиры и т.д. Требуются подписи различных чиновников, которые могут быть в отпуске или на больничном. Такой процесс может длиться очень долго: дни, недели, а то и месяцы. Именно для таких ситуаций создан state-machine workflow.
Что нам дает WF
Давайте теперь посмотрим, что нам дает WF. В первую очередь это наглядность. Все мы постоянно рисуем какие-то схемки, наброски. В более сложных случаях мы детально углубляемся в проектирование, например, рисуем временные диаграммы. Теперь мы можем перенести эти эскизы в Microsoft Visual Stuidio 2008 с помощью встроенного редактора. Вот как это выглядит:
Даже далекий от программирования человек сможет разобраться в том, что происходит на диаграмме.
Но все эти эскизы оторваны от действующей программы, никак с нею не взаимодействуют. Если нам надо изменить течение процесса, то необходимо открыть исходники, вспомнить места, в которых необходимо сделать изменения согласно новому эскизу. Эти задачи позволяет решить WF. Мы можем просто взять и перенести участок кода на другое место. Тут же его скомпилировать и сравнить с исходным вариантом.
Также использование WF помогает более наглядно представить работу системы. Выделить процессы, которые могут быть разделены на составляющие или наоборот, объединены. Также поддерживаются транзакционные системы: можно задать компенсирующий процесс для случая, когда что-то пошло не так. Этот процесс вернет систему в состояние, в котором она находилась до наступления ошибки.
Есть возможность опубликовать процесс в качестве сервиса или веб-сервиса, доступного через интернет. Более того, можно создать так называемый Durable web-service, который сохраняет идентификатор сессии и состояние сервиса в промежутках между вызовами, позволяя выполнять процесс в течение длительного времени, не поддерживая соединение. Вы даже можете остановить процесс, сохранить состояние и запустить его на другом компьютере с того же самого места.
Средства разработки
Наиболее удобным средством разработки является визуальный редактор, встроенный в Visual Studio 2008. Он позволяет быстро и гибко создавать и модифицировать процессы. При этом нет необходимости разбираться в том, как это устроено на низком уровне. Также можно создавать процессы, используя язык разметки XAML или при помощи C#.
Давайте взглянем на основные элементы интерфейса для работы с процессами в студии. Вот как выглядит окно в процессе разработки:
Рассмотрим назначение каждой панели.
Это основной документ, в котором мы моделируем процесс. Сейчас мы видим приглашение перетащить туда какие-нибудь элементы для создания последовательного процесса.
Процесс состоит из базовых элементов, называемых Activity. Они находятся в панели Toolbox под номером два. В ней находится множество элементов. Часть из них вполне понятна по их названию. Например, While или IfElse. ConditionedActivityGroup звучит более загадочно.
Естественно, понадобится панель Properties для настройки всевозможных параметров.
Также очень полезна панель Document Outline, обозначенная цифрой четыре. В ней отображается дерево элементов данного процесса, позволяющее быстро переходить от одного элемента к другому.
Заключение
WF предоставляет разработчику более высокий уровень абстракции при работе над проектом. Позволяет пробовать новые идеи, используя имеющиеся наработки. Вы можете создать библиотеку часто используемых процессов и их элементов для повторного применения в других проектах. В следующей статье мы познакомимся поближе с последовательными процессами на примере программы, осуществляющей резервное копирование файлов.
Начнем с истории
Наш флагманский продукт Avanpost IDM — это система управления жизненным циклом учетных записей и правами доступа работников. Управлять доступом он умеет как автоматически на основании ролевой модели, так и по заявкам. На заре становления продукта у нас была достаточно простая система самообслуживания с простым пошаговым workflow, для которого движок не требовался в принципе.
Однако, столкнувшись с крупными клиентами, мы поняли, что требуется гораздо более гибкий инструмент, поскольку их требования к процессам согласования прав доступа стремились к правилам хорошего развесистого документооборота. Проанализировав требования, мы решили разработать собственный редактор процессов в формате BPMN, подходящий под наши нужды. Про разработку редактора с использованием React.js + SVG мы расскажем чуть позже, а сегодня обсудим тему бэкенда — workflow engine или движка бизнес-процессов.
Требования
На момент начала разработки системы у нас были следующие требования к движку:
Windows Workflow Foundation (WWF)
WWF представляет собой технологию компании Microsoft для определения, выполнения и управления рабочими процессами.
Основу его логики составляет набор контейнеров для действий (активностей) и возможность из этих контейнеров строить последовательные процессы. Контейнер может быть обычным — некий шаг процесса, на котором выполняется активность. Может быть управляющим — содержащим в себе логику ветвления.
Процесс можно рисовать непосредственно в среде Visual Studio. Скомпилированная схема бизнес-процесса хранится в Хaml, что весьма удобно — формат описан, есть возможность сделать самописный дизайнер процессов. Это с одной стороны. А с другой — Xaml не самый удобный формат хранения описания — скомпилированная схема для более менее реального процесса получается огромной не в последнюю очередь из-за избыточности. Разобраться в ней очень сложно, а разбираться придется.
Но если со схемами рано или поздно можно постичь дзен и научится читать их, то вот отсутствие прозрачности работы самого движка добавляет хлопот уже во время эксплуатации системы пользователями. Когда ошибка исходит и недр Wf, узнать на 100%, в чем именно была причина сбоя, удается не всегда. Закрытость исходников и относительная монструозность делу не помогает. Часто фиксить баги приходилось по симптомам.
Справедливости ради тут стоит уточнить, что проблемы, описанные выше, по большей части преследовали нас из-за сильной кастомизации поверх Wf. Кто-нибудь из читателей точно скажет, что мы сами создали себе кучу проблем, а потом героически их решали. Нужно было делать самописный движок с самого начала. В целом, они будут правы.
Workflow Core
Workflow Core — это свободно распространяемый движок бизнес-процессов. Он разрабатывается под лицензией MIT, т.е его можно спокойно использовать в коммерческой разработке.
Активно делает его один человек, еще несколько периодически делают pull request. Есть порты на другие языки (Java, Python и еще несколько).
Движок позиционируется как легковесный. По сути это просто некий хост для последовательного выполнения сгруппированных по каким-либо бизнес-правилам действий.
У проекта есть документация в виде wiki. К сожалению, она описывает далеко не все возможности движка. Однако требовать полноценную документацию будет нагло — проект opensource, поддерживается одним энтузиастом. Поэтому Wiki вполне будет вполне достаточно для начала работы.
«Из коробки» есть поддержка хранения состояния процессов во внешних хранилищах (persistence storage). Стандартно идут провайдеры для:
Написать свой провайдер не составляет проблем. Берем исходники любого стандартного и делаем по примеру.
Поддерживается горизонтальное масштабирование, т.е можно запускать движок сразу на нескольких нодах, имея при этом одну точку хранения состояний процессов (один persistence storage). При этом размещение внутренней очереди задач движка должно быть в общем хранилище (rabbitMQ, как вариант). Для исключения выполнения одной задачи несколькими нодами одновременно предусмотрен диспетчер блокировок. По аналогии с провайдерами внешнего хранилища, есть стандартные реализации:
Знакомство с чем-то новым проще всего начать с примера. Так и поступим. Я опишу с самого начала построение простого процесса, попутно давая свои пояснения. Пример может показаться до невозможности простым. Соглашусь — он простой. Самое то для начала.
Step (Шаг)
Шаг — это этап процесса, на котором выполняются какие либо действия. Весь процесс строится из последовательности шагов. Один шаг может выполнять много действий, может выполняется повторно, например, по некоторому событию извне. Есть набор шагов, которые наделены логикой «из коробки»:
Само собой, на одних встроенных примитивах процесс не постоишь. Нужны шаги, которые выполняют бизнес-задачи. Поэтому пока отложим их в сторону и сделаем шаги со своей логикой. Для этого нужно наследоваться от абстракции StepBody.
Метод Run выполняется, когда процесс заходит в шаг. В него и нужно разместить необходимую логику.
Шаги поддерживают внедрение зависимостей. Для этого достаточно зарегистрировать их в том же контейнере, что и необходимые зависимости.
Очевидно, что процессу нужен свой контекст — место, куда можно складывать промежуточные результаты выполнения. В wf core есть свой контекст выполнения процесса, который хранит информацию о его текущем состоянии. Получить доступ к нему можно, используя переменную context из метода Run(). В дополнение ко встроенному мы можем использовать свой контекст.
Подробнее способы описания и регистрации процесса разберем чуть ниже, пока просто определим свой некий класс — контекст.
В переменные Number запишем числа; в переменную StepResult — результат выполнения шага.
С контекстом определились. Можно писать свой шаг:
Логика крайне простая: на вход приходят два числа и название операции. Результат операции записывается в выходную переменную Result. Если операция не определена, то результат будет none.
С контекстом мы определились, шаг с нужней нам логикой тоже есть. Теперь нужно зарегистрировать наш процесс в движке.
Описание процесса. Регистрация в движке.
Описать процесс можно двумя способами. Первый — это описание в коде — хардкод.
Непосредственно описание будет внутри метода Build. Поля Id и Version также необходимо заполнить. Wf core поддерживает версионность процессов — можно зарегистрировать n версий процесса с одинаковым идентификатором. Это удобно, когда требуется обновить существующий процесс и при этом дать «дожить» уже существующим задачам.
Опишем простой процесс:
Если перевести на «человеческий» язык, получится примерно так: процесс начинается с шага CustomStep. Значение поля шага Input1 берется из поля контекста Number1, Значение поля шага Input2 берется из поля контекста Number2, полю Action жестко указано значение «sum». Выходные данные из поля Result записывается в поле контекста StepResult. Завершить процесс.
Согласитесь, код получился весьма читаемый, вполне можно разобраться, даже не имея особых познаний в C#.
Добавим к нашему процессу еще один шаг, который будет выводить в лог результат выполнения предыдущего шага:
Теперь после шага с операцией сложения следует шаг вывода результата в лог. На вход мы передаем переменную Result и контекста, в которую на прошлом шаге записали результат выполнения. Возьму на себя смелость утверждать, что подобное описание через код (хардкод) в реальных системах будет малополезным. Разве что для каких-то служебных процессов. Куда более интересно иметь возможность хранить схему отдельно. Как минимум, нам не придется пересобирать проект каждый раз, когда нужно что-то поменять в процессе или добавить новый. Эту возможность wf core предоставляет посредством хранения схемы json. Продолжим расширять наш пример.
Json описание процесса
Далее я не буду приводить описание через код. Это не особо интересно, и только раздует статью.
Wf core поддерживает описание схемы в json. На мой взгляд, json более нагляден чем xaml (хорошая тема для холивара в комментариях 🙂 ). Структура файла довольно простая:
В поле DataType указывается полное имя класса контекста и имя сборки, в которой он описан. В Steps хранится коллекция всех шагов процесса. Заполним элемент Steps:
Давайте разберем подробнее структуру описание шага через json.
Поля Id и NextStepId хранят идентификатор данного шага и указатель, какой шаг может быть следующим. При этом порядок следования элементов коллекции неважен.
StepType аналогичен полю DataType, содержит полное имя класса шага (тип, который наследуется от StepBody и реализует логику шага) и название сборки. Дальше интереснее — объекты Inputs и Outputs. Они задаются в виде маппинга.
В случае Inputs имя элемента json — это имя поля класса нашего шага; значение элемента — имя поля в классе — контексте процесса.
Для Outputs наоборот, имя элемента json — это имя поля в классе — контексте процесса; значение элемента — имя поля класса нашего шага.
Разнообразим схему стандартными примитивами. Добавим условный шаг If и обработку внешнего события.
Примитив If. Тут начинаются сложности. Если вы привыкли к bpmn и рисуете процессы в этой нотации, то вас ждет легкая подстава. По документации шаг описывается следующим образом:
Нет ощущения, что что-то тут не так? У меня есть. На вход шага задается Condition — выражение. Дальше задаем список шагов внутри массива Do (действия). Так, а где ветка False? Почему нет массива Do для False? На самом деле есть. Подразумевается, что ветка False — это просто проход дальше по процессу, т.е по указателю в NextStepId. Первое время я постоянно путался из-за этого. Окей, тут разобрались. Хотя нет. Если действия по процессу в случае True нужно класть внутрь Do, это же какой «красивый» json тогда будет. А если там этих If вложенных с десяток? Все уедет вбок. А еще говорят, что схему на xaml трудно читать. Есть небольшой хак. Просто взять монитор пошире. Немного выше упоминалось, что порядок шагов в коллекции значение не имеет, переход идет по указателям. Это можно использовать. Добавим еще один шаг:
Догадываетесь, к чему я веду? Верно, мы вводим служебный шаг, который транзитом переводит процесс на шаг в NextStepId.
Обновим нашу схему:
В шаге If проверяется, пустой ли результат выполнения шага Eval. Если не пустой, то выводим результат, если пустой — то сообщение «Empty result». Шаг Jump переводит процесс в шаг Output, который находится вне коллекции Do. Таким образом, мы сохранили «вертикальность» схемы. Также таким способом можно переходить по условию на n шагов назад, т.е. организовывать цикл. В wf core есть встроенные примитивы для циклов, но они не всегда удобны. В bpmn, например, циклы организуются через If.
Использовать этот подход или стандартный, решать уже вам. Для нас такая организация оказалась шагов удобнее.
WaitFor
Примитив WaitFor дает возможность внешнему миру влиять на ход процесса, когда он уже запущен. Например, если на этапе процесса требуется одобрение дальнейшего хода каким-либо пользователем. Процесс будет стоять на шаге WaitFor, пока в него не придет событие, на которое он подписан.
Немного поясню параметры.
CancelCondition — условие прерывания ожидания. Предоставляет возможность прервать ожидание события и пойти дальше по процессу. Например, если процесс одновременно ждет n разных событий (wf core поддерживает параллельное выполнение шагов), ждать прихода всех не требуется, в этом случае нам поможет CancelCondition. Добавляем в переменные контекста логический флаг и при получении события выставляем флаг в значение true — все шаги WaitFor завершатся.
EventName и EventKey — имя и ключ события. Поля нужны, чтобы в реальной системе с большим количеством одновременно работающих процессов, различать события, т.е. чтобы движок понимал, какое событие для какого процесса и какого шага предназначено.
EffectiveDate — опциональное поле, добавляет событию метку времени. Может пригодится в случае, если нужно опубликовать событие «в будущее». Чтобы оно опубликовалось сразу параметр можно оставить пустым или задать текущее время.
Далеко не во всех случаях удобно делать отдельный шаг для обработки реакций извне, скорее даже обычно он будет избыточен. Лишнего шага можно избежать, добавив в обычный шаг ожидание внешнего события и логику его обработки. Дополним шаг CustomStep подпиской на внешнее событие:
Мы воспользовались стандартным методом расширения WaitForEvent(). Он принимает на вход уже упомянутые ранее параметры EventName, EventKey и EffectiveDate. После выполнения логики такого шага процесс встанет в ожидание описанного события и снова вызовет метод Run() в момент публикации события в шине движка. Однако в текущем виде мы не можем различить моменты первичного входа в шаг и вход после события. А хотелось бы как-то разделить логику до-после на уровне шага. И в этом нам поможет флаг EventPublished. Он находится внутри общего контекста процесса, получить его можно так:
Опираясь на этот флаг можно спокойно разделить логику на до и после внешнего события.
Важное уточнение — по задумке создателя движка один шаг может быть подписать только на один эвент и среагировать на него один раз. Для некоторых задач это весьма неприятное ограничение. Нам даже пришлось «допиливать» движок, чтобы от этого нюанса уйти. Сейчас в этой статье их описание пропустим, иначе статья никогда не закончится :). Более сложные практики использования и примеры доработок будут освещаться в последующих статьях.
Регистрация процесса в движке. Публикация события в шину.
Итак, с реализацией логики шагов и описания процесса разобрались. Осталось самое главное, без чего процесс не будет работать — описание нужно зарегистрировать.
Воспользуемся стандартным методом расширения AddWorkflow(), который разместит в нашем IoC контейнере свои зависимости.
IServiceCollection — интерфейс — контракт коллекции описаний сервисов. Он живет внутри DI от Microsoft (подробнее про него можно почитать тут)
WorkflowOptions — базовые настройки движка. Самому их задавать не обязательно, стандартные значение вполне приемлемы для первого знакомства. Едем дальше.
Если процесс описывался в коде, то регистрация происходит так:
Если процесс описан через json, то его нужно регистрировать так (само собой, json описание нужно предварительно загрузить из места хранения):
Далее для обоих вариантов код будет одинаков:
Параметр definitionId — идентификатор процесса. То, что записано в поле Id процесса. В данном случае идентификатор = SomeWorkflow.
Параметр version указывает, какую версию процесса запустить. Движок предоставляет возможность регистрировать сразу n версий процесса с одним идентификатором. Это удобно, когда требуется внести изменения в описание процесса, не ломая уже запущенные задачи — новые будут создаваться по новой версии, старые спокойно доживут на старой.
Параметр context — экземпляр контекста процесса.
Методы host.Start() и host.Stop() запускают и останавливают хостинг процессов. Если в приложении запуск процессов — прикладная задача и выполняется периодически, то следует останавливать хостинг. Если приложение имеет основным направлением выполнение различных процессов, то хостинг можно не останавливать.
Для передачи сообщений из внешнего мира в шину движка, которая потом распределит их по подписчикам есть метод:
Описание его параметров было выше в статье (см. часть про WaitFor примитив).
Заключение
Мы определенно рисковали, когда приняли решение в пользу Workflow Core — opensource проекта, который активно разрабатывает один человек, да еще и с весьма бедной документацией. И реальных практик использования wf core в рабочих системах (кроме нашей) вы, скорее всего, не найдете. Конечно, выделив отдельный слой абстракций, мы подстраховались на случай неуспеха и необходимости быстро вернутся к WWF, например, или самописному решению, но все пошло вполне неплохо и неуспех не настал.
Следом идет открытость исходников. Работая с WWF и получая разнообразные ошибки из его недр, возможность хотя бы почитать исходники была бы очень кстати. Не говоря уже о том, чтобы что-то в них поменять. Тут с Workflow Core полная свобода (в том числе по лицензированию — MIT). Если вдруг появляется ошибка из недр движка, просто качаем исходники из github и спокойно ищем причину ее возникновения. Да просто возможность запустить движок в режиме отладки с точками останова уже сильно облегчает процесс.
Само собой, решив одни проблемы, Workflow Core принес уже свои, новые. Нам пришлось внести ощутимое количество изменений в ядро движка. Но. Работы по «допиливанию» под себя обошлись дешевле по времени, чем разработка собственного движка с нуля. Итоговое решение получилось вполне приемлемо по скорости и стабильности работы, позволило нам на текущий момент забыть про проблемы с движком и сфокусироваться на развитии бизнес-ценности продукта.