Системный анализЧеклист

Состояния и переходы до разработки

Практический материал для аналитика и тимлида: как собрать модель состояний, таблицу переходов и запрещенные сценарии до разработки, тестирования и интеграции.

·7 минут чтения·
состоянияпереходысистемный анализUMLинтеграцииприемка

Когда использовать

Модель состояний нужна, когда у объекта или операции есть жизненный цикл, а команда по-разному понимает:

  • что означает текущий статус;
  • кто и каким событием может его изменить;
  • какие переходы допустимы;
  • что считается финальным результатом;
  • можно ли вернуться назад;
  • что происходит после таймаута, ошибки или отмены;
  • какие состояния видят пользователь, оператор и внешняя система;
  • где нужен ручной разбор.

Это бывает в заявках, заказах, платежах, документах, согласованиях, фоновых операциях и внешних интеграциях.

Статус - не подпись на экране

Статус влияет на поведение системы:

  • какие действия доступны;
  • можно ли редактировать данные;
  • допустим ли повтор;
  • кто отвечает за следующий шаг;
  • какие уведомления отправляются;
  • когда запускается таймер;
  • что попадает в мониторинг;
  • какой результат увидит другой участник.

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

Начать с объекта и границы

До списка статусов ответьте:

Чей жизненный цикл описываем?

Где хранится его текущее состояние?

Кто считает это состояние источником истины?

Какие соседние объекты имеют отдельный жизненный цикл?

Например, статус пользовательской заявки и статус отправки во внешний контур - не одно и то же. Заявка может оставаться “на проверке”, пока техническая операция переживает таймаут, повтор или ручной разбор. Если смешать два объекта в одном поле, продуктовый смысл начинает зависеть от транспортной ошибки.

Шаг 1. Собрать состояния из рабочего языка

Источники состояний:

  • действия пользователя и оператора;
  • ответы внешней системы;
  • правила редактирования;
  • ошибки и таймауты;
  • ручные обходы;
  • уведомления;
  • отчеты и фильтры;
  • вопросы поддержки “где сейчас заявка”;
  • фразы вроде “уже нельзя изменить” или “нужно дождаться”.

Для каждого кандидата спросите:

  • поведение системы действительно меняется;
  • есть отдельный владелец следующего действия;
  • состояние нужно хранить и восстанавливать;
  • пользователь или поддержка должны его различать;
  • по нему принимается решение о повторе, отмене или завершении.

Если ничего не меняется, возможно, это не состояние, а причина, отметка, атрибут или текст сообщения.

Шаг 2. Отделить состояния от событий

Состояние отвечает на вопрос “где объект сейчас”. Событие отвечает на вопрос “что произошло”.

Состояние Возможное событие
draft Пользователь отправил заявку
checking Проверка нашла недостающие данные
needs_changes Пользователь исправил данные
approved Проверка завершилась успешно
rejected Найдено основание для отказа
cancelled Пользователь или оператор отменил процесс

Фразы sent, timeout и retry могут быть как событиями, так и состояниями - это зависит от модели. Важно договориться, что хранится, а что только вызывает переход.

Шаг 3. Нарисовать только разрешенный жизненный цикл

stateDiagram-v2
  direction TB

  state "Черновик" as draft
  state "Проверка" as checking
  state "Одобрено" as approved
  state "Отказ" as rejected

  [*] --> draft
  draft --> checking: отправили
  checking --> approved: условия выполнены
  checking --> rejected: есть основание
  approved --> [*]
  rejected --> [*]

Для первой схемы достаточно основного финального контура. Возврат на доработку, отмену, таймауты и другие ветки стоит добавлять только тогда, когда они остаются читаемыми и помогают обсуждению.

Диаграмма показывает форму жизненного цикла, но не заменяет спецификацию. Условия, владельцы, побочные действия и ошибки удобнее проверять в таблице.

Шаг 4. Собрать таблицу переходов

Минимальные колонки:

Было Событие Условие Стало Кто меняет Побочное действие
draft Заявку отправили Обязательные поля заполнены checking Пользователь / система Зафиксировать версию данных
checking Не хватает данных Разрешена доработка needs_changes Проверяющий Отправить понятный список замечаний
needs_changes Данные исправили Проверка формата пройдена checking Пользователь / система Создать новую попытку проверки
checking Условия выполнены Нет блокирующих причин approved Проверяющий / правило Зафиксировать решение и уведомить
checking Найдено основание Код причины известен rejected Проверяющий / правило Сохранить безопасное объяснение
draft Процесс отменили Обработка еще не началась cancelled Пользователь Не запускать проверку

Для интеграции добавьте:

  • внутренний и внешний идентификатор;
  • idempotency key;
  • число попыток;
  • время последнего события;
  • источник изменения;
  • correlation id;
  • признак ручного действия.

Эти поля не обязаны жить в одной таблице базы. Они должны быть видимы в аналитической модели и контракте.

Шаг 5. Явно перечислить запрещенные переходы

Разрешенная схема не всегда показывает, что именно команда должна защищать. Отдельно выпишите опасные переходы:

Запрещенный переход Почему опасен Как защищаем
draft -> approved Проверка пропущена Разрешать одобрение только из checking
rejected -> checking Финальное решение переписано без нового основания Создавать пересмотр или новую версию отдельно
cancelled -> approved Отмененный процесс оживает после позднего ответа Игнорировать поздний ответ и записывать диагностическое событие
approved -> needs_changes Финальный результат становится неясным Использовать отдельный процесс отзыва или корректировки
Любое состояние -> неизвестный статус Потребитель не понимает новое значение Валидировать контракт и определить поведение совместимости

Запрещенный переход стоит превратить в acceptance criterion или автоматическую проверку, если его цена высока.

Шаг 6. Разобрать таймаут, повтор и поздний ответ

Таймаут не означает, что действие не произошло. Команда должна решить:

  • остается ли объект в ожидании;
  • появляется ли отдельное техническое состояние;
  • можно ли проверить результат по идентификатору;
  • допустим ли автоматический повтор;
  • как повтор защищен от дубля;
  • что делать с ответом, который пришел после отмены или ручного решения;
  • когда процесс переходит к оператору;
  • кто получает сигнал о зависшем состоянии.

Для дорогих операций полезно разделять:

  1. продуктовый статус объекта;
  2. статус технической попытки;
  3. решение о повторе;
  4. ручное действие.

Так пользовательский результат не меняется напрямую из-за сетевой детали.

Шаг 7. Определить финальность

Финальное состояние не всегда означает “никогда не изменится”. Оно означает, что текущий процесс завершен и продолжение требует отдельного правила.

Для каждого финального состояния зафиксируйте:

  • можно ли начать пересмотр;
  • создается новая версия или меняется старая;
  • что видят внешние потребители;
  • какие уведомления уже отправлены;
  • можно ли компенсировать результат;
  • какие данные остаются доступными для аудита;
  • кто имеет право запустить следующий процесс.

Не стоит возвращать финальный объект в середину старого жизненного цикла без явного решения. Это усложняет аудит и делает историю неоднозначной.

Шаг 8. Связать переходы с приемкой

Для важного перехода нужен проверяемый сценарий:

Дано:
заявка находится в состоянии checking.

Когда:
внешняя проверка не отвечает до таймаута.

Тогда:
- заявка не становится approved или rejected;
- техническая попытка получает диагностируемый результат;
- повтор не создает вторую операцию;
- поддержка видит correlation id и время попытки;
- пользователь получает безопасное сообщение без технических деталей.

Проверьте как минимум:

  • основной успешный путь;
  • предметный отказ;
  • отмену;
  • нехватку данных;
  • таймаут;
  • повтор одного события;
  • события в неправильном порядке;
  • поздний ответ;
  • попытку перейти из финального состояния;
  • неизвестное значение внешнего статуса.

Шаг 9. Назначить владельца перехода

У перехода должен быть источник:

  • пользователь;
  • оператор;
  • бизнес-правило;
  • фоновая задача;
  • внешний контур;
  • таймер;
  • администратор;
  • процесс компенсации.

Фраза “система переводит” слишком общая, если непонятно, какой компонент проверяет условие и сохраняет результат. Для спорных переходов полезно зафиксировать владельца в API-контракте или записи решения.

Короткий шаблон

Объект:
Источник истины:

Состояния:
- название;
- смысл;
- кто видит;
- доступные действия;
- финальное или промежуточное.

Переходы:
- было;
- событие;
- условие;
- стало;
- владелец;
- побочные действия.

Запрещенные переходы:
- ...

Ошибки и ожидание:
- таймаут;
- повтор;
- поздний ответ;
- ручной разбор.

Приемка:
- основной путь;
- альтернативы;
- неправильный порядок;
- повтор;
- финальность.

Открытые вопросы:
- ...

Антипаттерны

  • один статус одновременно описывает бизнес-результат и техническую попытку;
  • список статусов есть, но переходы не определены;
  • переход может выполнить “система”, но компонент и условие неизвестны;
  • error скрывает предметный отказ, таймаут и ручной разбор;
  • финальные состояния меняются как обычные;
  • поздний ответ переписывает отмененное или подтвержденное решение;
  • новый статус внешнего API молча считается успешным;
  • диаграмма есть, но QA и поддержка не могут получить из нее сценарии;
  • в логах хранится больше данных, чем нужно для диагностики;
  • модель пытается описать все будущие состояния до проверки первого контура.

Куда идти дальше

Модель состояний нужна не ради полного каталога статусов. Она делает видимыми правила, которые иначе обнаруживаются только в коде, тестах или инциденте.

Дальше

Продолжить по теме

Следующий шаг — открыть материал «Как выбрать диаграмму: процесс, поведение или архитектура» и расширить этот чеклист или подход соседним практическим контекстом.

Открыть материал