Во фреймворке HLEB2 есть возможность выполнять асинхронные запросы, что накладывает дополнительные требования к коду. Одно из основных требований - нужно избавляться от хранимого состояния при завершении запроса.
К хранимому состоянию могут относиться данные текущего пользователя, кеш данных запроса, всевозможная мемоизация и т.д.
В программировании мемоизация — это метод оптимизации, который делает уже вычисленные данные переиспользуемыми. Способ состоит в том, чтобы сохранить в кэше выходные данные метода класса и заставить метод проверять, находится ли каждое требуемое вычисление в кэше, прежде чем вычислять его.
Необходимо определить, какие хранимые состояния относятся к данным запроса, а какие к работе конкретного приложения в целом. Например, вычисленное состояние для общей информации о тарифах не будет изменяться от запроса к запросу, а выбранный тариф для каждого пользователя необходимо сбрасывать. При асинхронных запросах следующий запрос может принадлежать другому пользователю и поэтому важно очистить информацию о предыдущем.
Современные практики программирования не рекомендуют пользоваться состоянием, хранимым как статическое свойство класса, но часто оно удобно и забота о нём возникает только при переходе на асинхронный режим.
Чтобы этот переход был удобным во фреймворке HLEB2 есть специальный интерфейс RollbackInterface с одним статическим методом rollback.
Например, есть такое хранимое состояние с данными текущего пользователя (упрощённый код):
Чтобы состояние сбросилось, добавлен интерфейс RollbackInterface и реализован метод rollback:
Теперь при завершении асинхронного запроса фреймворк проверит, есть ли у класса интерфейс RollbackInterface и выполнит метод сброса rollback. Необходимо принять во внимание, что метод, сбрасывающий состояние, должен быть идемпотентным и не делать ничего больше. То есть при повторном выполнении результат применения не будет другим.
Для чего нужна идемпотентность видно из следующего, более сложного примера, в котором интерфейс применяется в наследовании (метод сброса может быть вызван дважды):
Если необходимо выполнить какое-либо действие по завершению асинхронного запроса, не связанное со сбросом состояния в конкретном классе, то его можно добавить в метод rollback класса App\Bootstrap\ContainerFactory.