Какие подходы к управлению состоянием вы предпочитаете (NgRx, Akita, Signals)?
Подход к управлению состоянием в Angular зависит от масштаба проекта, требований к предсказуемости, отладки, количества источников данных и необходимости в реактивности. На практике я использую разные подходы: от простого сервисного слоя с BehaviorSubject, до полной архитектуры с NgRx, Akita или Signals. Ниже описаны предпочтения и сценарии использования каждого подхода.
1. Простой сервис с BehaviorSubject
Для небольших приложений или фич, которые не требуют сложного управления состоянием, использую обычные Angular-сервисы с BehaviorSubject или ReplaySubject.
Пример:
@Injectable({ providedIn: 'root' })
export class AuthService {
private userSubject = new BehaviorSubject<User | null>(null);
user$ = this.userSubject.asObservable();
setUser(user: User) {
this.userSubject.next(user);
}
logout() {
this.userSubject.next(null);
}
}
Плюсы:
-
Простота реализации;
-
Хорошо работает в изолированных модулях;
-
Нет лишнего boilerplate.
Минусы:
-
Нет строгой структуры;
-
Не масштабируется при росте приложения;
-
Нет devtools/таймтревела.
2. NgRx
Использую NgRx в крупных, корпоративных приложениях, где:
-
Много взаимодействий с сервером;
-
Требуется строгое разделение ответственности;
-
Важна предсказуемость и отладка состояний (DevTools);
-
Сложная бизнес-логика;
-
Необходим таймтревел, undo-redo, state replay.
Пример структуры:
store/
├── auth/
│ ├── auth.actions.ts
│ ├── auth.effects.ts
│ ├── auth.reducer.ts
│ └── auth.selectors.ts
├── user/
└── app.state.ts
Плюсы:
-
Предсказуемость и однонаправленный поток данных;
-
Интеграция с Effects для обработки побочных эффектов;
-
Инструменты разработки (Redux Devtools);
-
Строгая архитектура;
-
Возможность time-travel, логирования и записи состояния.
Минусы:
-
Много шаблонного кода (boilerplate);
-
Крутая кривая обучения;
-
Чрезмерно тяжёлый для простых приложений.
С появлением NgRx Component Store возможно использовать NgRx в локальном скоупе без глобального store, что удобно для feature-модулей.
3. Akita
Использую Akita, когда нужна реактивная архитектура с меньшим количеством шаблонного кода, но при этом требуется централизованное управление состоянием с разветвлённой логикой и селекторами.
Пример:
@Injectable({ providedIn: 'root' })
export class TodoStore extends EntityStore<TodoState, Todo> {
constructor() {
super();
}
}
Плюсы:
-
Менее шаблонный и более декларативный код по сравнению с NgRx;
-
Простота создания EntityStore;
-
Отличная документация;
-
Локальные или глобальные сторы;
-
Поддержка transaction/undo/history.
Минусы:
-
Менее строгий подход;
-
Нет такой экосистемы и сообщества, как у NgRx;
-
Сложнее для команд, привыкших к Flux-архитектуре.
Akita отлично подходит для проектов, где есть rich-data model и нужно управлять большим количеством сущностей.
4. Signals + SignalStore (Angular 16+)
С выходом Signals (signal, computed, effect) и экспериментального @angular/signals/store появляется современная альтернатива RxJS и NgRx.
Пример:
export class CounterStore {
count = signal(0);
double = computed(() => this.count() \* 2);
increment() {
this.count.set(this.count() + 1);
}
}
Signal Store:
@Injectable()
export class TodosStore extends signalStore({ todos: \[\] }) {
readonly completed = this.select(s => s.todos.filter(t => t.completed));
add(todo: Todo) {
this.update(state => ({
todos: \[...state.todos, todo\]
}));
}
}
Плюсы:
-
Императивный стиль, ближе к JavaScript;
-
Высокая производительность;
-
Нет подписок, нет .subscribe();
-
Простой синтаксис;
-
Локальные или глобальные сторы;
-
Потенциальная замена NgRx/BehaviorSubject.
Минусы:
-
Пока не production-ready во всех аспектах;
-
Ограниченный инструментарий (DevTools, Debugging);
-
Меньшее количество обучающих материалов;
-
Возможна путаница с change detection на ранней стадии.
Подходит для новых проектов на Angular 17+ и для feature-модулей без необходимости в сложном state management.
5. Сравнение подходов
Подход | Применимость | Boilerplate | Реактивность | DevTools | Сложность |
---|---|---|---|---|---|
BehaviorSubject | Мелкие модули | Низкий | Да | Нет | Низкая |
--- | --- | --- | --- | --- | --- |
NgRx | Крупные проекты | Высокий | Да (RxJS) | Да | Высокая |
--- | --- | --- | --- | --- | --- |
Akita | Средние/крупные | Средний | Да (RxJS) | Да | Средняя |
--- | --- | --- | --- | --- | --- |
Signals | Новые модули, SPA | Низкий | Да (Signals) | Ограничено | Средняя |
--- | --- | --- | --- | --- | --- |
NgRx Component Store | Средние проекты | Средний | Да | Да | Средняя |
--- | --- | --- | --- | --- | --- |
6. Комбинированный подход
В крупных приложениях я использую комбинацию:
-
Signals — для UI-логики или компонентов;
-
ComponentStore — для feature-модулей;
-
NgRx — для глобального состояния;
-
Akita — если проект исторически на нём и нужно управление сущностями.
При этом соблюдается принцип: "используй минимально необходимое" — чем меньше глобального состояния, тем легче поддержка.