Как стать автором
Обновить

Комментарии 26

Заменить примитивные if на конфигурирование еще одной библиотечки? В чем плюсы то, ну кроме того, что вас не уволят, потому что никто кроме вас в этом коде не разберется?

Спорное утверждение. Данный инструмент должен уменьшить объем кода и как следствие проект меньше завязан на разработчика. Инфу же по этой либе любой может легко в доках прочитать.
Запутанная в ifах логика же наоборот разработчика делает более «бесценным» так как только он понимает, что там происходит
Спасибо за статью. Скажите а данная машина состояний персистентна? Что будет если приложение остановили, когда процесс был в каком — то промежуточном статусе(не в начальном и не в end), а затем включим: процесс будет потерян, продолжится или как?
PS: если это было в статье и я не заметил ответ на мой вопрос — прошу прощения
Добрый день. Тут будет зависеть от scope машины, если она singleton, и она находится в каком то промежуточном статусу, то она будет находиться в нем до тех пор пока не остановится спринговый контекст, либо пока вы явно не скажете ей .stop()(после этого она перейдет в конечное состояние, которое вы закодировали). В случае если вы получаете машину из фабрики, то при каждом обращении к сервису будет создаваться новая машина, соответственно при каждом запросе вы сохраняете ее состояние в базу или в Map, как у меня в примере, а при повторном запросе восстанавливаете ее, то есть если машина в промежуточном состоянии и у вас настроен персистинг, то вы вернетесь в то же самое промежуточное состояние.
Добрый день! Спасибо за статью. У меня продолжение для предыдущего вопроса. Если машина была остановлена когда уже получила новый евент, но ещё не успела обработать его можно ли как-то зареплаить этот евент при восстановлении чтобы не перепосылать (тут вопрос наверно про транзакционность и DR)? И есть ли возможность каких-то STP. Например для мы кинули евент RESERVED, а машина дальше допинала через BUY по определённому условию?
мне кажется то о чем вы говорите нарушает концепцию машины в целом, если машина была остановлена, и вы после этого бросили эвент, то он не должен ее касаться, то есть у вас явно что то не по плану пошло, вы можете проверять стутус машины после восстанновления, и выставлять его принудительно в тот какой вам нужно, но это уже какое то костыльное решение имхо будет
Ок. Вопрос про STP остаётся актуален. По первому вопросу пример такой. Допустим машина получила RESERVED, записала в БД состояние, после клиент кинул BUY, машина его получила, прошёл платёж, а дальше сервис упал. После восстановления будем иметь неконсистентный заказ. Если клиент ещё раз кинет BUY, то деньги спишутся ещё раз, но и служба доставки его не получила. Как всё таки допинать до терминального статуса такой кейс?
В вашем случае можно (нужно) декомпозировать модель состояний. Т.е. «деньги списаны» и «запрос доставлен в службу доставки» должны быть разными состояниями со своими переходами и логикой.
Если я правильно вас понял, то на одно состояние у вас завязаны две независимые логики, что не есть хорошо.
А здесь уже возникает опять вопрос STP. Так как из «деньги списаны» в «запрос в доставке» переход должен проходить автоматически, т.е. сервис после восстановления работы должен сам решать такие кейсы, но есть много примеров, в которых не имеет смыслка декомпозиция иначе получится «Запрос на резервирование получен» -> «Запрос на резервирование доставлен в обработчик» -> «Запрос на резервирование в обработке» -> «Запрос на резервирование обработан» -> «Товар зарезервирован». И эти степы никакого отношения к бизнес процессу иметь не будут
Согласен. Не стоит всё доводить до абсурда, в том числе и дробление модели состояний.
В вашем примере уже фигурирует before, after, in_progress что само по себе уже явно намекает, что так дробить состояния не нужно и пора бы уже остановиться.
В примере выше — две независимые бизнес-логики тем более общающиеся (скорее всего) с разными внешними системами.
Тут можно решение обыграть через бронирование.Т.е. перед оплатой бронируется позиция на складе, идет оплата, бронь подтверждается. При таком подходе у системы шансов больше выжать и не потерять заказ (и товар).

переход должен проходить автоматически, т.е. сервис после восстановления работы должен сам решать такие кейсы

Если такое требование есть — то, возможно, лучшим решением будет использовать BPMN движки или Саги.

Бывает, что достаточно фонового процесса следящего за «подвисшими» сущностями и запускающий альтернативную цепочку состояний.
Допустим вы находитесь в статусе RESERVED, совешаете покупку, летит Event по которому должно произойти списание денег, и переход в статус BUY, логика на списание заложена в неком Action, вы делаете метод на списание транзакционным, навешиваете Action в конфигурации States, я в статье писал про этонемного, так вот, там сработка Action будет немного по другому, вы сначала зайдете в статус, а уже потому выполнится ваш транзакционный метод, если вдруг в нем случится ошибка машина вернется в предыдущее состояние, и списания не произойдет. Там разные комбинации на который можно навешивать ваши Action, stateEntry, stateExit, state, то есть можно настроить так, чтоб перестраховаться от того что вы упали сразу после списания
А является ли Spring State Machine альтернативой для Spring WebFlow?
Они немного в параллельных плоскостях лежат. Спринт ВебФлоу скорее про переходы между страничками в браузере и имеет большое количество завязок на http-протокол и веб сервер в целом.
Интересная статья, не знал что в spring и такое уже завезли.
А для решения подобных задач вы не смотрели в сторону bpmn или drools? Если в первой, как мне кажется, сложные графы переходов РИСОВАТЬ проще, то вторая дюже удобна, когда много мелкой логики.
у нас в проекте был реализован паттерн State, сложновато конечно выглит(много дженериков), но работает как часы и добавление новой логики на разных этапах нашего бизнес кейса получилось довольно простое и прозрачное. Когда пришла очередь добавлять StateMachine для другого кейса, я точно с таким же удивлением как и вы обнаружил что есть решение от Spring. Ну и на нашу логику все просто идеально легко, так что по сторонам уже даже и не смотрели, у нас намечается третий кейс в проекте, который так же будет решаться Spring машиной, и вот огромным плюсом будет то, что вы сможем часть компонентов из предыдущей машины задействовать. Машина по сути это именно некий каркас в вашей бизнес-логике, вы можете в нее заинжектать любые сервисы, которые дергали бы из if-ов.
Скорее всего статья на хабре была моя :-) (StateMachine в протоколе РОСЭУ).
У нас логика перехода состояний документа в документообороте сейчас на ней сделана. Работой вполне довольны. Единственно что не сразу поняли — в экшны лучше по минимуму логики вкладывать и просто больше экшнов делать.

Удобно, что через пару замен в коде можно UML диаграмму получить. И наоборот — UML диаграмму легко в код перевести.

Кстати, в sendEvent можно отправить не только ивент, но и Message с ивентом. В хедерах которого можно протащить доп информацию и использовать ее в экшенах.

А так в целом — спасибо за более подробный разбор библиотеки. На мой взгляд стейт машина на много лучше, чем куча if/else и switch.
Доброго времени суток. Про Message совершенно верно подметили, есть такая возможность, но нам показалось более удобным передавать информацию через контекст. По поводу Action у нас появилось понимание довольно быстро, как только стало ясно что в конфигурации tarnsition можно навешивать много экшенов на один переход.
Статья была Ваша, извиняюсь если чем то обидел, поверьте, даже в мыслях не было. А так, ваш проект получается третий из тех что я знаю(наш, ваш, и еще в Nexign) где спринг стейт машина работает на проде.)
Смотрю, и вижу прямо то, что буквально вчера думал, как и где найти. Сейчас как раз размышляю над стейт-машиной для обеспечения воркфлоу взаимодействия учётной системы предприятия с государственными учётными системами, типа ФГИС Меркурий. На первый взгляд — ровно то, что надо, и даже сохранение состояний в базу. И всё же терзают сомнения…

Насколько оправданным может быть использование данной библиотеки вне Спринга, в проекте на Java EE? Не подтянет ли тонну лишних спринговых зависимостей?
Как реализовать prototype в singlton-е?

ObjectFactory. Да, сервис придётся завязывать на спринговый интерфейс, но это лучше, чем завязка на контекст.

Плюсом идёт отсутствие необходимости в поднятии спрингового контекста для тестирования.

Ещё можно через BeanFactoryAware запилить, но это, по-сути, та же завязка на контекст приложения.
Основное преимущество описанного мной подхода в том что это будет возможность протестировать машину через unit-тест, а не через интеграционный или функциональный, если у вас в интеграционном тесте перед каждым тестом в тестовом профиле накатываются схемы в БД, таблицы и прочее прочее прочее, то ваши интеграционные тесты со временем нормально так затягиваются, и вы точно предпочтете тестировать через Unit, если будет такая возможность
для таких задач лучше использовать Akka. Имхо.

Тоже подумал про FSM но есть ощушение что затаскивание без всей akka инфраструкту́ры не лучшая идея.

Спасибо за статью. Использовал Spring StateMachine для создания простого лексического анализатора. Интересует вопрос, насколько оправдано использование Spring StateMachine в данном случае?
думаю что вопрос насколько оправдано использование того или иного фремворка — вопрос относительный, все зависит от сложности скорее всего. Для несложных задач, скорее всего не стоит использовать, вы скорее всего усложните на ровном месте… но вот для более сложных эта штука становится серьезным подспорьем, попробуйте реализовать какой нибудь многоступенчатый сложный процесс без паттернов(например процедуру оформления дистанционного кредита), это будет сложно и запутанно, вы сами начнете думать в сторону какого то паттерна, а тут уже готовое решение. Машина в первую очередь это архитектурное решение.
Если вы свою задачу декомпозировали и разложили на концепцию машины, то почему бы и нет.
Просто в каждой статье про лексический анализатор упоминается, что лучше всего его делать на конечных автоматах. А тут уже готовая реализация. По моим скромным впечатлениям, после первого знакомства, использование спринговской стейт машины сильно упростило написание логики анализатора, чем придумывание собственного костыля.

Хотя формально, можно добавить в эти enum поля, и захардкодить в них что-нибудь, свойственное например, конкретному state, что довольно логично(мы так и поступили решая свой кейс, довольно удобно).

А покажите, как?

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории