Для начала давайте определимся с терминологией. Состояние приложение - это набор данных приложения, или модель данных, которая необходима для верной работы приложения. Она может в себя включать как все данные приложения, так и лишь некоторые.
Библиотек состояния приложения сейчас довольно много и это обоснованно. Состояние приложения - это очень удобно: всегда есть нечто вроде "карты" состояния всего и сразу; в любой момент можно понять что и как: какое окно открыто, какой текст введён в поле ввода или какие привилегии есть у пользователя после авторизации. Это очень удобно для маленьких приложений! Однако, состояния, чаще всего, обновляются через события. И это быстро приводит к хаосу.
Появляется много различных типов событий для самых малозначительных случаев, требующих ответной части - триггеров. А триггеры требуют обработки и преобразования данных. Нередко, проблема усугубляется асинхронностью или параллельными вычислениями. Типы событий множатся, уследить за всеми становится совершенно невозможно, появляются дублёры, а часть событий вообще перестаёт быть актуальной.
И это не говоря про то, что для запуска некоторых типов событий могут потребоваться пользовательские права или привилегии, а закладывать контроль в логику триггеров - весьма накладно (триггеров много и контроль нужен едва ли не в каждом). Да даже если вопрос разделения доступа не брать во внимание, проблемы растут как снежный ком по мере разрастания набора состояний приложения.
И что делать?
Однако выход есть и он сводится не к библиотеке, а к методологии использования состояния.
Использовать разные наборы состояний
Для каждого отдельного модуля приложения без возможности изменения состояния в модуле А из модуля Б.
По сути - сведение всего приложения к набору мини-приложений, каждое со своим состоянием. Получается много маленьких моделей состояния, но не всегда это возможно и не всегда уместно.
Иерархическая модель состояний с каскадной изоляцией состояний
Если по-человечески, то модель состояний - дерево, где каждый новый уровень - модель для "нижележащего" модуля. При этом вышестоящий модуль знает всё о состояниях нижележащих, а нижележащие не имеют доступа к данным вышестоящих модулей, зато имеют функцию обратного вызова, уведомляющую о том, что модуль что-то изменил данные и, возможно, закончил свою работу. Подходит для иерархически-выстроенных приложений, но не очень подходит для случая, когда требуется горизонтальная интеграция (модули соседних веток общаются напрямую): например, таблица поставок контрагентов и словарь контрагентов (второй может потребоваться если надо внести новую поставку, а контрагента нет в списке). Причина проста - пробросы данных между "ветками" требуют значительных накладных расходов по всей цепочке. Не говоря уже о том, что запросто могут возникнуть случаи "универсальных полей", когда одно и то же состояние используется в нескольких модулях, но глобальным быть не может: куда его надо отнести?
Плоская модель: все имеют доступ ко всему
Если не вдаваться в подробности, то получится полнейшая лапша и настоящий ад в документации. С другой стороны, появляется замечательная возможность, скажем, менять системные настройки приложения из раздела справки. Удобно? Удобно! Безопасно? Совершенно нет! Ну и тысяча строк с описанием всех возможных состояний приложения сильно сбивают мотивацию и поддерживаемость кода. Правда это можно решить, разнеся данные по разным файлам, склеивая их при сборке.
Минимальная модель состояний
Вот это уже интересней: ведь нам, для отслеживания состояния приложения, необязательно знать о всех возможных его данных (хотя это было бы не лишним). Храним только "системообразующие" параметры, вроде ключевых справочников и параметров, а все остальные данные обрабатываем как удобно, сохраняя их, при необходимости, любым удобным способом, либо вовсе сбрасывая, после того, как потребность в них исчезла. Да, будут издержки на перезагрузку данных, зато в состояниях не запутаемся и поддержка кода возрастёт.
И это я рассмотрел лишь наиболее простые и понятные случаи. А ведь можно добавить системы обработки состояний со сложной программной логикой и специализированным функционалом изменения и реакций на изменения данных.
Заключение
Как видим, использование состояний в любом случае сопровождается некоторый набором трудностей, на которые надо решиться в самом начале пути, сделав трудный выбор. Если принять во внимание тот факт, что запланированная архитектура приложения, со временем, устареет (по причине роста и развития), то, скорее всего, изначально выбранная модель, в какой-то момент, напрочь перестанет отвечать требованиям к использованию и из "ракетного ранца" превратится в "чемодан без ручки", который придётся поддерживать героическими усилиями или убирать из проекта.
И, отвечая на главный вопрос из заголовка статьи - как же использовать состояния приложения и не сойти с ума: никак. Но это не значит, что состояние приложения - абсолютное зло. Просто соглашаясь на него, надо быть готовым к последствиям.