State Management
MobX: архитектура
В приложении растёт состояние: пользователь, корзина, каталог, уведомления. Свалить всё в один гигантский стор означает потерять границы предметных областей. Разнести по несвязанным глобальным переменным означает потерять способ дотянуться от корзины до текущего пользователя. MobX предлагает архитектурный ответ из мира OOP: каждая предметная область это отдельный стор-класс, а корневой root store держит их вместе и раздаёт ссылки друг на друга.
- Доменные модели интернет-магазина: UserStore, CartStore, CatalogStore под одним root store
- Финансовые и учётные приложения с богатой бизнес-логикой в классах сущностей
- Редакторы и CRM, где сущности связаны ссылками и методами друг на друга
- Команды с бэкграундом в OOP, которым ближе классы и инкапсуляция, чем редьюсеры
- Проекты, где сериализация и снимки состояния важны и берут MobX-State-Tree
Предварительные знания
- observable, computed и action как строительные блоки стора
- Идея инкапсуляции: данные и поведение в одном классе
- Понимание, зачем нужен единый доступ к разным частям состояния
Доменные сторы и root store
Базовый приём архитектуры на MobX это деление состояния по предметным областям. Каждая область получает свой стор-класс: UserStore, CartStore, CatalogStore. Внутри стора живут только данные и логика своей области. Это прямое следование принципу единственной ответственности: стор корзины не знает деталей каталога, а лишь обращается к нему через ссылку.
Чтобы доменные сторы могли дотягиваться друг до друга, их собирают под корневым root store. RootStore создаёт каждый доменный стор и передаёт ему ссылку на себя. Через эту ссылку любой стор находит соседей: стор корзины через root доходит до текущего пользователя, чтобы посчитать скидку.
Ссылка root исключается из наблюдения через { root: false }, потому что сам root store не должен быть observable-значением: это контейнер, а не данные. Наблюдаемыми остаются поля доменных сторов, а root лишь даёт навигацию между ними.
В React один экземпляр root store обычно прокидывают через Context, а компоненты достают нужный доменный стор хуком. Так root остаётся единой точкой сборки, а компоненты подписываются на конкретные доменные сторы, а не на весь граф состояния сразу.
Зачем доменные сторы собирают под корневым root store?
OOP-стиль и богатые предметные модели
Сила MobX раскрывается, когда предметная область богата поведением, а не только данными. Сущность вроде заказа или проекта это класс: поля хранят состояние, computed выводят производные характеристики, методы-action меняют состояние по правилам домена. Логика живёт рядом с данными, которыми управляет, а не разнесена по редьюсерам и селекторам.
Здесь домен говорит сам за себя. total и canPlace это computed: они выводятся из строк и статуса и пересчитываются сами. Метод place меняет статус только если canPlace, и правило перехода инкапсулировано в классе. Компонент не дублирует эту логику: он читает canPlace и вызывает place, а корректность держит модель.
- Логика размазана по UI — Условие можно ли разместить заказ проверяется в компоненте. То же правило копируется в нескольких местах и рассинхронизируется
- Логика в доменной модели — canPlace и place живут в классе Order. Одно место истины для правила, компоненты лишь читают и вызывают
Именно за это MobX ценят команды с OOP-бэкграундом: предметная модель выражается классами с инкапсуляцией и поведением, как в типичном backend-домене. Реактивность добавляется одним вызовом в конструкторе и не размывает структуру модели на action-creator и reducer.
В чём выгода держать правило can ли разместить заказ как computed canPlace внутри класса Order, а не проверять его в компоненте?
MobX-State-Tree и где MobX силён
Поверх MobX существует надстройка MobX-State-Tree, сокращённо MST. Она описывает состояние не свободными классами, а типизированными узлами дерева: у каждой модели объявлен тип полей, есть представления (аналог computed) и действия. За счёт строгой структуры MST даёт из коробки снимки состояния, сериализацию в JSON и обратно, патчи изменений и middleware. Это полезно там, где состояние нужно сохранять, восстанавливать и отслеживать по шагам.
MST берут, когда важны снимки и сериализация: сохранить всё дерево состояния, восстановить его, проиграть изменения. Чистый MobX берут, когда нужна свобода классов и не нужна жёсткая типизация узлов. Граница выбора между ними проходит по тому, насколько важны структурные возможности дерева против гибкости обычного OOP.
| Где MobX силён | Где MobX слабее |
|---|---|
| Богатые предметные модели со связями и поведением | Простое плоское UI-состояние: одна модалка, тема |
| OOP-домены, близкие к backend-сущностям | Команды без OOP-бэкграунда, которым ближе атомы |
| Автоматическое отслеживание сложного графа зависимостей | Сценарии, где важна явная иммутабельность и time-travel |
| Сериализация и снимки через MST | Минимальный бандл, где важнее лёгкий стор |
Честная граница: для простого клиентского состояния вроде открытой панели или выбранной темы тяжесть классов и root store избыточна, и лёгкий стор вроде Zustand часто читается проще. MobX окупается там, где предметная модель богата поведением и связями, а не там, где состояние это пара флагов.
В каком сценарии архитектура на доменных сторах MobX окупается сильнее всего?
Связь с другими темами
Этот урок про архитектуру состояния на MobX. Рядом стоят соседи:
- MobX: observable, computed, action — Доменные сторы это классы на observable, computed и action из базового урока
- MobX: reactions — Реакции мостят доменные сторы с персистентностью, логами и синхронизацией
Итог
- Состояние делят на доменные сторы: по стор-классу на предметную область (пользователь, корзина, каталог)
- Корневой root store держит доменные сторы вместе и раздаёт им ссылки друг на друга
- OOP-стиль: данные это поля, производные это computed, изменения это методы-action внутри класса
- Связи между сторами идут через ссылку на root store, а не через глобальные переменные
- MobX-State-Tree (MST) надстройка с явными типами узлов, снимками и сериализацией из коробки
- MobX силён на богатых предметных моделях со связями и поведением, слабее на простом плоском UI-состоянии
Связанные уроки
- sm-22-mobx-observables — Доменные сторы строятся на observable, computed и action из базового урока MobX
- sm-23-mobx-reactions — Реакции связывают доменные сторы с эффектами вроде персистентности и синхронизации