- PVSM.RU - https://www.pvsm.ru -
Одной из неотъемлемых частей любой ECM-системы [1] является управление бизнес-процессами, или workflow.
Бизнес-процессы в каждой отдельной организации имеют множество нюансов. Они постоянно изменяются вследствие изменений внутри организации, изменений законодательства и т.д. Поэтому дешевле и логичнее к разработке бизнес-процессов привлекать либо аналитиков, либо программистов, специализирующихся на бизнес-логике. А значит, создание и изменение бизнес-процессов должно быть максимально простым и удобным.
Так же при изменении процесса уже запущенные процессы должны корректно работать. Нельзя останавливать долгое и сложное согласование договора только потому, что теперь согласованный документ должен распечатать не инициатор согласования, а секретарь.
Это диктует некоторые требования, которые предъявлялись к движку бизнес-процессов:
При разработке новой версии движка бизнес-процессов мы решили попробовать Windows Workflow Foundation [2] (далее WF).
Каждый высокоуровневый блок маршрута может состоять из большого количества Activity (Например, для блока задания нужно 68 активностей). Это связано с тем, что каждый блок имеет несколько событий, в обработчики которых можно писать код. Так же для каждой части блока (события, внутренняя логика блока) должна работать обработка ошибок. Обработка эта делает следующее: если было брошено исключение, то оно анализируется, и в некоторых случаях нужно не прерывать процесс, а попытаться еще раз через некоторое время. Причем время ожидания до следующей попытки постепенно возрастает от 5 минут до 1 часа. Это нужно для ситуаций, когда не удалось совершить операцию из-за проблем со связью, таймаута SQL сервера и т.д.
Можно было бы сделать блоки составными активностями, но WF не позволяет делать активности с несколькими исходящими стрелками. Например, блок маршрута «Задание на согласование документа» должен выглядеть следующим образом:
А WF позволяет сделать только так:
Причем еще придется делать переменную и передавать через нее результат выполнения задания.
Вторая проблема – блоки, выполняемые параллельно. Единственный способ сделать это в WF – использовать блок Parallel. Но тогда вместо интуитивного:
Мы получаем:
Все это привело нас к тому, что нам не достаточно активностей WF как таковых, нам нужна схема более высокого порядка, которая описывает маршрут «сверху». При разработке маршрута используются наши классы блоков (никак не связанные с WF), а уже потом готовая схема конвертируется в Activity. Схемы процессов хранятся в виде XML, генерация Activity происходит в момент публикации маршрута на сервер приложения. Кроме блоков схемы содержат связи между блоками (стрелки из одного блока в другой).
Для каждого блока есть парный класс билдера, который генерирует активность. Выглядит это примерно так:
public override System.Activities.Activity BuildContent()
{
var result = new Variable<bool>(this.Block.ResultVariableName);
return new Sequence()
{
Variables =
{
result
},
Activities =
{
new Assign
{
To = new OutArgument<bool>(this.result),
Value = new InArgument<bool>(false)
},
//...
new Persist()
}
};
}
Составные активности мы не используем, чтобы не иметь проблем с конвертацией.
Единственная сложность в конвертации маршрута, описанного нашими блоками, состоит в параллельных ветках. Такие ветки маршрута обрабатываются отдельно, потом результат объединяется в Parallel.
Конвертация процесса WF происходит в несколько этапов:
Основная проблема здесь – невозможность создать UpdateMap на основе двух Activity. Т.е. если на одном сервере развернута версия 1, на другом – версия 2, на третьем – версия 3, то обновиться на версию 5 будет проблематично. Еще сложнее будет обновить первый сервер с версии 1 на версию 4.
Схемы на сервере хранятся в виде XML, в котором лежат наши блоки, а не активности. Таким образом, конвертировать нужно с одной версии нашего представления маршрута на другую. Это происходит так:
Изменение Activity в пункте 4 происходит так: сгенерированная для блока активность упаковывается в FlowStep (если из блока выходит несколько стрелок с условиями, то после FlowStep генерируется FlowDecision). При изменении/добавлении/удалении связей изменяются значения свойств FlowStep.Next.
Каждый блок хранится в переменной в схеме в сериализованном виде. При изменении свойств блока меняется дефолтное значение этой переменной.
При добавлении блока генерируется соответствующий набор активностей и вставляется в нужное место схемы. Удаление блока – это просто очистка FlowStep.Next, который в него ведет.
Кроме изменения бизнес-процесса, конвертация может потребоваться и при изменении генерируемых для блока активностей. Например, если нужно добавить новый функционал в блок, или просто исправить баг. Мы сделали это так:
Каждая схема маршрута хранит версию алгоритма генерации Activity.
При изменении логики генерации активности для блока версия увеличивается, а конвертер учится конвертировать активности этого блока со старого варианта на новый.
При конвертации маршрута конвертер конвертирует активности блоков, для которых изменилась логика генерации (определяется по версии схемы).
Единственная особенность – конвертация так же должна проходить в виде изменения существующих активностей, а не генерации с нуля, иначе UpdateMap не подхватится.
После прочтения статьи может создаться впечатление, что мы зря использовали Workflow Foundation – это не так. Благодаря использованию WF мы получили из коробки
В статье описано лишь решение проблемы низкоуровневости WF. За кадром остались вопросы
Автор: lsreg
Источник [4]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/net/74449
Ссылки в тексте:
[1] ECM-системы: https://en.wikipedia.org/wiki/Enterprise_content_management
[2] Windows Workflow Foundation: https://ru.wikipedia.org/wiki/Windows_Workflow_Foundation
[3] хостинг: https://www.reg.ru/?rlink=reflink-717
[4] Источник: http://habrahabr.ru/post/243103/
Нажмите здесь для печати.