← Retour au blog
AngularNgRxState ManagementSignalsArchitecture

NgRx SignalStore vs Signal-Based Services: Which Architecture to Choose in 2026

NgRx SignalStore represents a departure from traditional RxJS-based NgRx. Where classic stores rely on observables and an action-reducer-selector architecture, SignalStore leverages Angular's signals for more direct reactivity and more predictable change detection. Simultaneously, a lighter alternative emerges: a business service exposing writable and computed signals, without a state management framework. The choice between these approaches isn't ideological, but contextual—rooted in the actual complexity of your business domain and maintenance constraints.

SignalStore: when structure pays off

SignalStore offers an opinionated structure: features with local and global state, declarative effects, out-of-the-box state serialization. This framework forces coherent organization, particularly valuable in team projects or codebases exceeding 50,000 lines. SignalStore's writable and computed signals enable very granular state derivation. If you manage an e-commerce cart with dynamic VAT calculations, currency conversions, and server sync states, SignalStore imposes a clear path: effects handle HTTP calls, actions describe business intentions, computed signals maintain coherent derived views. This eliminates an entire class of bugs tied to temporal inconsistencies between multiple sources of truth.

The learning curve remains steep. Angular developers accustomed to RxJS find SignalStore effects counter-intuitive at first: they mix declarative and imperative styles, with re-execution logic based on signal dependencies. Debugging an effect that triggers too often or at the wrong time requires understanding the underlying dependency graph. For migrations from classic NgRx stores, the investment is significant, but the gains in performance and change detection simplicity justify the effort in medium-to-large projects.

Signal-based services: pragmatism and lightness

A simple business service with writable and computed signals offers a convincing alternative for 70% of real-world use cases. Take a concrete example: a task list service. Instead of creating a SignalStore, you expose a writable signal for tasks, a computed signal for completed task count, and methods that mutate state directly. This approach eliminates state management friction: no actions to dispatch, no effects to orchestrate, no state serialization to configure. The code is shorter, more readable for junior developers, and state changes are immediate and traceable with a simple tap on the signal.

Signal-based services shine in low-entropy domains: local UI management (modal open/closed, filter applied), simple client-side data caching, form state. Here's a minimal example:

export class TaskService {

private tasks = signal<Task[]>([]);

public tasks$ = this.tasks.asReadonly();

public completedCount = computed(() =>

this.tasks().filter(t => t.done).length

);

addTask(title: string) {

this.tasks.update(arr => [...arr, { id: nanoid(), title, done: false }]);

}

toggleTask(id: string) {

this.tasks.update(arr =>

arr.map(t => t.id === id ? { ...t, done: !t.done } : t)

);

}

}

This service fits in fifty lines. Zero NgRx boilerplate, no actions created, no declarative effects. Components consume tasks$ and completedCount() directly via OnPush detection. For most business applications, it's sufficient and more maintainable than an oversized SignalStore infrastructure.

Real pitfalls and edge cases

The first pitfall: confusing initial ease with long-term scalability. A signal-based service works perfectly until you need to synchronize state across multiple pages, implement smart cache invalidation, or add local persistence. At that point, structural absence becomes technical debt. You end up with services calling each other, ad hoc cache invalidation patterns, and no clear trace of data flow. SignalStore enforces hygiene that prevents these situations.

The second pitfall: ignoring the cost of NgRx dependency. SignalStore adds roughly 40 KB gzipped to your bundle. If your application totals 200 KB gzipped, this dependency matters. For a small internal dashboard or micro-frontend, the signal-based service stays lighter. Measure your actual bundle, not your assumptions.

A third pitfall, specific to SignalStore: effects executing unexpectedly. If you declare a dependency on a frequently changing signal (like a search field), your effect can trigger dozens of times per second. You must learn to debounce, use pipeables like tapResponse, and master effect lifecycle to avoid memory leaks and cascading HTTP calls. A poorly guided junior developer can create a nightmare of circular dependencies and unnecessary re-renders.

Practical decision criteria

Use SignalStore if: you manage more than three distinct business domains, your state depends on multiple async sources (WebSocket, polling, user events), you need time-travel debugging or state persistence, or your team has more than four developers working on the same feature. The initial investment pays off quickly.

Stick with signal-based services if: your application is mostly local (few or no async HTTP calls), business state is simple and loosely dependent on other states, you work alone or in a very small group, or you face strict bundle size constraints. Pragmatism wins here.

A hybrid approach also exists: use SignalStore for critical business domains (cart, authentication, shared data), and signal-based services for local UI (modals, filters, pagination). This combines architectural rigor where it matters and lightness where possible.

Conclusion: choose by context, not by trend

In 2026, NgRx SignalStore is no longer an ideological choice, but a pragmatic one. It solves real coherence and performance problems in complex applications. However, it doesn't replace a well-designed signal-based service for 70% of common use cases. Start by honestly evaluating your business domain's complexity: do you truly need an action for every user intention? Does your state depend on multiple nested async sources? If the answers are no, a signal-based service suffices and you save maintenance effort. If yes, SignalStore becomes a sound investment. Measure your bundle, test both approaches on a non-critical feature, and decide with full knowledge. That's pragmatic architecture.

Développeur Angular & Mobile freelance — Strasbourg.

© 2026 Emilien Pons — Tous droits réservés.Conçu avec Angular, PrimeNG et ❤️