NgRx Signal Store vs NgRx Clássico em 2026: Qual Escolher?
Comparação detalhada entre NgRx Signal Store e NgRx clássico para gerenciamento de estado Angular em 2026. Conheça as diferenças arquiteturais, exemplos de código e dicas para escolher a solução correta.

NgRx Signal Store representa uma mudança fundamental no gerenciamento de estado do Angular, substituindo o padrão tradicional inspirado no Redux (actions-reducers-selectors) por uma abordagem reativa baseada em signals. Esta comparação examina ambas as soluções para ajudar desenvolvedores Angular a tomar decisões arquiteturais informadas em 2026.
Usar Signal Store para estado em nível de funcionalidade e aplicações de complexidade média onde a redução de boilerplate importa. Usar NgRx Clássico para aplicações empresariais que requerem trilhas de auditoria rigorosas, efeitos colaterais complexos e depuração extensiva com DevTools.
Entendendo as Diferenças Arquiteturais
NgRx clássico segue o padrão Redux: as actions descrevem eventos, os reducers produzem novo estado imutável, e os selectors extraem dados. Cada mudança de estado flui através de um pipeline previsível e rastreável.
Signal Store adota uma abordagem diferente. Em vez de despachar actions através de reducers, o estado vive em signals reativos. As atualizações acontecem através de métodos que chamam patchState(), e os valores derivados vêm de signals computados em vez de selectors memorizados.
| Aspecto | NgRx Clássico | Signal Store | |---------|---------------|---------------| | Modelo Mental | Redux/Flux | Serviços Reativos | | Boilerplate | Alto (actions, reducers, selectors, effects) | Baixo (state, methods, computed) | | Reatividade | Observables RxJS | Signals Angular | | DevTools | Redux DevTools completo | Signal Store DevTools (via plugin) | | Curva de aprendizado | Íngreme | Moderada | | Tamanho do Bundle | Maior | Menor |
Para desenvolvedores já familiarizados com Signals do Angular, Signal Store parece uma extensão natural das primitivas reativas do framework.
NgRx Clássico: O Padrão Redux no Angular
NgRx clássico organiza o gerenciamento de estado em torno de actions, reducers, selectors e effects. Essa separação impõe previsibilidade ao custo de verbosidade.
import { createActionGroup, emptyProps, props } from '@ngrx/store';
import { Book } from './book.model';
export const BooksActions = createActionGroup({
source: 'Books',
events: {
'Load Books': emptyProps(),
'Load Books Success': props<{ books: Book[] }>(),
'Load Books Failure': props<{ error: string }>(),
'Add Book': props<{ book: Book }>(),
'Remove Book': props<{ id: string }>(),
},
});A API createActionGroup agrupa actions relacionadas sob uma única fonte, melhorando a legibilidade no DevTools e reduzindo o boilerplate comparado com chamadas individuais de createAction.
import { createReducer, on } from '@ngrx/store';
import { BooksActions } from './books.actions';
import { Book } from './book.model';
export interface BooksState {
books: Book[];
loading: boolean;
error: string | null;
}
const initialState: BooksState = {
books: [],
loading: false,
error: null,
};
export const booksReducer = createReducer(
initialState,
on(BooksActions.loadBooks, (state) => ({
...state,
loading: true,
error: null,
})),
on(BooksActions.loadBooksSuccess, (state, { books }) => ({
...state,
books,
loading: false,
})),
on(BooksActions.loadBooksFailure, (state, { error }) => ({
...state,
loading: false,
error,
}))
);Os reducers continuam sendo funções puras que retornam novos objetos de estado. A função on() lida com a correspondência de actions e atualizações de estado de forma concisa.
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { BooksState } from './books.reducer';
export const selectBooksState = createFeatureSelector<BooksState>('books');
export const selectAllBooks = createSelector(
selectBooksState,
(state) => state.books
);
export const selectBooksLoading = createSelector(
selectBooksState,
(state) => state.loading
);
export const selectPublishedBooks = createSelector(
selectAllBooks,
(books) => books.filter((book) => book.published)
);Os selectors fornecem memorização automaticamente. O selector selectPublishedBooks só recalcula quando o array de livros muda, tornando as leituras eficientes mesmo em stores atualizados frequentemente.
NgRx Signal Store: Gerenciamento de Estado Signals-First
Signal Store consolida estado, valores computados e métodos em um único serviço injetável. A documentação oficial do NgRx o descreve como uma "solução de gerenciamento de estado completamente equipada com suporte nativo para Signals do Angular."
import { computed, inject } from '@angular/core';
import { patchState, signalStore, withComputed, withMethods, withState } from '@ngrx/signals';
import { BooksService } from './books.service';
import { Book } from './book.model';
interface BooksState {
books: Book[];
loading: boolean;
error: string | null;
}
const initialState: BooksState = {
books: [],
loading: false,
error: null,
};
export const BooksStore = signalStore(
{ providedIn: 'root' },
withState(initialState),
withComputed(({ books }) => ({
publishedBooks: computed(() => books().filter((book) => book.published)),
totalBooks: computed(() => books().length),
})),
withMethods((store, booksService = inject(BooksService)) => ({
async loadBooks() {
patchState(store, { loading: true, error: null });
try {
const books = await booksService.getAll();
patchState(store, { books, loading: false });
} catch (error) {
patchState(store, { loading: false, error: 'Failed to load books' });
}
},
addBook(book: Book) {
patchState(store, ({ books }) => ({ books: [...books, book] }));
},
removeBook(id: string) {
patchState(store, ({ books }) => ({
books: books.filter((book) => book.id !== id),
}));
},
}))
);Todo o gerenciamento de estado de uma funcionalidade cabe em um único arquivo. As propriedades de estado se tornam signals automaticamente, signals computados substituem selectors, e métodos substituem despachos de actions.
Gerenciamento de Entidades com withEntities
Ambas as abordagens suportam coleções de entidades, mas a funcionalidade withEntities do Signal Store reduz drasticamente o código.
import { computed, inject } from '@angular/core';
import { patchState, signalStore, withComputed, withMethods } from '@ngrx/signals';
import {
addEntity,
removeEntity,
setAllEntities,
updateEntity,
withEntities,
} from '@ngrx/signals/entities';
import { BooksService } from './books.service';
import { Book } from './book.model';
export const BooksEntityStore = signalStore(
{ providedIn: 'root' },
withEntities<Book>(),
withComputed(({ entities }) => ({
publishedBooks: computed(() => entities().filter((book) => book.published)),
})),
withMethods((store, booksService = inject(BooksService)) => ({
async loadBooks() {
const books = await booksService.getAll();
patchState(store, setAllEntities(books));
},
addBook(book: Book) {
patchState(store, addEntity(book));
},
updateBook(id: string, changes: Partial<Book>) {
patchState(store, updateEntity({ id, changes }));
},
removeBook(id: string) {
patchState(store, removeEntity(id));
},
}))
);A funcionalidade withEntities fornece automaticamente os signals ids, entityMap e entities. Os atualizadores de entidades como addEntity, removeEntity e updateEntity lidam com mutações de coleções.
Pronto para mandar bem nas entrevistas de Angular?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Features de Store Personalizadas para Reusabilidade
A função signalStoreFeature do Signal Store permite extrair e reutilizar preocupações transversais entre stores.
import { computed } from '@angular/core';
import { signalStoreFeature, withComputed, withState } from '@ngrx/signals';
type RequestStatus = 'idle' | 'pending' | 'fulfilled' | 'error';
export function withRequestStatus() {
return signalStoreFeature(
withState<{ requestStatus: RequestStatus }>({ requestStatus: 'idle' }),
withComputed(({ requestStatus }) => ({
isPending: computed(() => requestStatus() === 'pending'),
isFulfilled: computed(() => requestStatus() === 'fulfilled'),
isError: computed(() => requestStatus() === 'error'),
}))
);
}
export function setPending(): { requestStatus: RequestStatus } {
return { requestStatus: 'pending' };
}
export function setFulfilled(): { requestStatus: RequestStatus } {
return { requestStatus: 'fulfilled' };
}
export function setError(): { requestStatus: RequestStatus } {
return { requestStatus: 'error' };
}Esta feature personalizada agora pode ser composta em qualquer store que precise de rastreamento do estado de carregamento.
import { inject } from '@angular/core';
import { patchState, signalStore, withMethods } from '@ngrx/signals';
import { setAllEntities, withEntities } from '@ngrx/signals/entities';
import { withRequestStatus, setFulfilled, setPending, setError } from './with-request-status';
import { BooksService } from './books.service';
import { Book } from './book.model';
export const BooksStore = signalStore(
{ providedIn: 'root' },
withEntities<Book>(),
withRequestStatus(),
withMethods((store, booksService = inject(BooksService)) => ({
async loadBooks() {
patchState(store, setPending());
try {
const books = await booksService.getAll();
patchState(store, setAllEntities(books), setFulfilled());
} catch {
patchState(store, setError());
}
},
}))
);O store composto agora expõe os signals computados isPending(), isFulfilled() e isError() sem duplicar lógica.
Padrões de Integração em Componentes
Signal Store se integra naturalmente com a sintaxe de templates do Angular através de leituras de signals.
import { Component, inject } from '@angular/core';
import { BooksStore } from './books.store';
@Component({
selector: 'app-books-list',
standalone: true,
template: `
@if (store.isPending()) {
<div class="loading-spinner">Carregando livros...</div>
}
@if (store.isError()) {
<div class="error-message">Falha ao carregar livros</div>
}
@for (book of store.entities(); track book.id) {
<div class="book-card">
<h3>{{ book.title }}</h3>
<p>{{ book.author }}</p>
<button (click)="store.removeBook(book.id)">Remover</button>
</div>
}
<p>Total: {{ store.totalBooks() }} livros</p>
`,
})
export class BooksListComponent {
readonly store = inject(BooksStore);
constructor() {
this.store.loadBooks();
}
}Sem gerenciamento de subscriptions. Sem pipes async. Os signals se integram com a detecção de mudanças zoneless do Angular para performance otimizada.
Considerações de Performance e Tamanho de Bundle
A arquitetura mais leve do Signal Store se traduz em bundles menores. O pacote core @ngrx/signals adiciona aproximadamente 4KB comprimido com gzip, comparado com 15-20KB para o stack completo do NgRx clássico (@ngrx/store, @ngrx/effects, @ngrx/entity).
Signal Store também se beneficia da detecção de mudanças baseada em signals do Angular. Signals computados só recalculam quando as dependências mudam, e componentes só re-renderizam quando signals consumidos são atualizados. Esta reatividade granular elimina ciclos desnecessários de detecção de mudanças comuns em padrões baseados em Observables.
Para aplicações que já usam RxJS extensivamente, os selectors baseados em Observables do NgRx clássico se integram perfeitamente com pipelines existentes.
Estratégia de Migração de Clássico para Signal Store
Migrar uma aplicação NgRx clássica existente não requer uma reescrita completa. Ambas as soluções podem coexistir, permitindo adoção incremental.
- Começar com novas funcionalidades usando Signal Store
- Migrar módulos de funcionalidades isolados um de cada vez
- Manter funcionalidades complexas com muitos effects no NgRx clássico se o custo de migração exceder o benefício
- Usar os utilitários de interoperabilidade do NgRx Signals para conectar estado baseado em Observables e baseado em signals
NgRx Store clássico e Signal Store podem rodar lado a lado na mesma aplicação. O time do NgRx suporta oficialmente ambas as abordagens e fornece utilitários de interoperabilidade para conectá-las.
Quando Escolher Cada Abordagem
Escolher Signal Store quando:
- Se está construindo novas aplicações Angular 20+
- Se está gerenciando estado em nível de funcionalidade ou componente
- Experiência do desenvolvedor e redução de boilerplate são prioridade
- O time tem experiência limitada com NgRx
- O tamanho do bundle importa
Escolher NgRx Clássico quando:
- Se está mantendo aplicações NgRx existentes
- Se requer trilhas de auditoria rigorosas com logging de actions
- Se está construindo fluxos de trabalho complexos com efeitos colaterais extensivos
- Se precisa de depuração time-travel completa do Redux DevTools
- O time tem experiência profunda em NgRx
Para os fundamentos de gerenciamento de estado, entender ambas as abordagens prepara desenvolvedores para diversos codebases Angular.
Comece a praticar!
Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.
Conclusão
- Signal Store reduz o boilerplate de gerenciamento de estado entre 60-70% comparado com NgRx clássico
- NgRx clássico continua sendo a melhor escolha para aplicações empresariais que requerem trilhas de auditoria rigorosas e efeitos colaterais complexos
- Ambas as abordagens podem coexistir na mesma aplicação, permitindo migração incremental
- Os signals computados do Signal Store fornecem memorização automática sem definições explícitas de selectors
- As features de store personalizadas (
signalStoreFeature) permitem preocupações transversais reutilizáveis - Para novos projetos Angular 20+ em 2026, Signal Store deve ser a escolha padrão a menos que requisitos específicos demandem NgRx clássico
Compartilhar
Artigos relacionados

RxJS no Angular 2026: operators, Subjects e interop com Signals
RxJS no Angular 2026: domine os operators, os Subjects e os padrões de interop com Signals usados em produção, além das perguntas de entrevista mais frequentes.

Angular @defer em 2026: Lazy Loading Declarativo e Entrevistas
Domine os blocos @defer do Angular para lazy loading declarativo no nível de componente. Guia completo sobre gatilhos, prefetching, hidratação incremental, comportamento SSR e padrões de performance para aplicações reais.

Angular 20 em 2026: Resource API, httpResource e perguntas de entrevista
O Angular 20 introduz o httpResource e estabiliza a Resource API para a busca de dados baseada em signals. Um tutorial prático sobre resource(), rxResource(), httpResource(), a validação com Zod e as perguntas de entrevista mais comuns.