← Back to Articles         
Service-Based State vs NgRx

Service-Based State vs NgRx

Published on 01 Oct 2026     10 min read Angular
State Management

1. Service-Based State vs NgRx — When Should Each Be Used?

Both approaches can manage shared Angular state, but they solve different levels of complexity.

The easiest way to remember:

Small / Medium complexity
        ↓
Service + Signals / RxJS

Large / Complex state interactions
        ↓
NgRx

Service-Based State

A service acts as the owner of shared state.

Full Simple Example

import {
  Injectable,
  signal,
  computed
} from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class CartStateService {

  private readonly _items =
    signal<string[]>([]);

  readonly items =
    this._items.asReadonly();

  readonly count =
    computed(() =>
      this._items().length
    );

  addItem(item: string): void {

    this._items.update(items => [
      ...items,
      item
    ]);

  }

  removeItem(item: string): void {

    this._items.update(items =>
      items.filter(x => x !== item)
    );

  }

  clear(): void {

    this._items.set([]);

  }

}

Components inject:

readonly cart =
  inject(CartStateService);

and use:

Cart: {{ cart.count() }}

The architecture is:

              CartStateService
               Shared Signal
              /      |       \
             /       |        \
         Header     Cart    Checkout

When Should You Use Service-Based State?

Use a service with Signals/RxJS when:

  • The state is relatively simple.
  • Only a few components share the state.
  • State transitions are straightforward.
  • You don't need formal Action/Reducer architecture.
  • You want less boilerplate.
  • Your team can easily understand where state changes occur.

Examples:

Current user UI state
Cart count
Theme
Sidebar state
Selected filters
Search criteria
Simple application settings

NgRx State

NgRx introduces a formal state flow:

Component
    ↓
Action
    ↓
Reducer
    ↓
Store
    ↓
Selector
    ↓
Component

With APIs:

Component
    ↓
Action
    ↓
Effect
    ↓
ASP.NET Core API
    ↓
Success Action
    ↓
Reducer
    ↓
Store
    ↓
Selector
    ↓
Component

When Should You Use NgRx?

NgRx becomes useful when you have:

  • Large amounts of shared state.
  • Many components interacting with the same state.
  • Complex state transitions.
  • Complex asynchronous workflows.
  • Many related actions.
  • Strong requirements for traceability.
  • Large teams needing consistent state-management patterns.
  • Complex derived state.
  • State behavior that needs extensive unit testing.

For example:

Enterprise Application
│
├── Authentication
├── Users
├── Permissions
├── Products
├── Orders
├── Payments
├── Customers
├── Notifications
└── Reports

NgRx can provide a consistent structure for managing these feature states.


Service vs NgRx

Service + Signals/RxJS NgRx
Simpler More structured
Less boilerplate More boilerplate/concepts
Easy to learn Higher learning curve
Good for small/medium state Good for complex state
Methods modify state Actions describe changes
Service owns state Store owns managed state
Signals/RxJS expose state Selectors expose state
API calls often in services Effects commonly orchestrate API calls
Easy to start Strong conventions

Interview Rule

Don't say:

"Large application means NgRx."

Size alone isn't the deciding factor.

A large application with mostly independent features may work well with services.

A better answer is:

Use NgRx when the complexity of shared state, state transitions, asynchronous workflows, debugging, and coordination justifies its additional architecture.


2. Signals vs NgRx — What Is the Difference?

This comparison is important because Signals and NgRx aren't exactly the same category of technology.

Signals

Signals are Angular's reactive primitives.

For example:

count = signal(0);

They allow us to:

count();

read state,

count.set(10);

replace state,

and:

count.update(
  value => value + 1
);

update state.


NgRx

NgRx is a broader state-management ecosystem/architecture.

It provides concepts such as:

Store
Action
Reducer
Selector
Effect

Therefore:

Signals
   ↓
Reactive values


NgRx
   ↓
Structured state-management architecture

Signal Example

@Injectable({
  providedIn: 'root'
})
export class CounterState {

  private readonly _count =
    signal(0);

  readonly count =
    this._count.asReadonly();

  increment(): void {

    this._count.update(
      count => count + 1
    );

  }

}

Flow:

Component
    ↓
CounterState.increment()
    ↓
Signal
    ↓
UI

Very simple.


NgRx Equivalent

The conceptual flow is:

Component
    ↓
dispatch Increment Action
    ↓
Reducer
    ↓
Store
    ↓
Selector
    ↓
Component

More structure is involved.


Signals vs NgRx

Signals NgRx
Angular reactive primitive State-management ecosystem
Stores/reacts to values Provides structured state architecture
signal() Store
set() / update() Actions + Reducers
computed() Selectors/derived state concepts
Simple API More concepts
Excellent for local state Useful for complex shared state
Excellent in state services Useful for formal enterprise state flows
No Action required Action-driven patterns available
No Reducer required Reducers central to classic Store
No Effect required Effects commonly handle side effects

The rows aren't strict one-to-one equivalents—they illustrate the different abstraction levels.


Can Signals and NgRx Work Together?

Yes.

Don't think:

Signals VS NgRx

as though selecting one permanently excludes the other.

Modern Angular/NgRx applications can use Signals alongside NgRx patterns.

For example:

Local UI State
      ↓
Signals


Complex Shared Business State
      ↓
NgRx

A component could use a local Signal:

isMenuOpen = signal(false);

while the application uses NgRx for:

Products
Orders
Authentication state
Permissions
Complex workflows

That's perfectly reasonable.


3. What State Should Be Kept on the Client vs ASP.NET Core Server?

This is the most important architectural question of the three.

A useful principle is:

Angular may keep UI and working state, but ASP.NET Core/database should remain authoritative for security-sensitive and persistent business data.


Client-Side State

Angular can manage state required primarily for the current user interface.

Examples:

Selected tab
Menu open/closed
Search text
Sort order
Current page number
Selected product
Temporary form state
Loading status
Validation display state
Filters
Modal visibility

For example:

searchText = signal('');

selectedCategory = signal<number | null>(null);

isLoading = signal(false);

currentPage = signal(1);

These are natural client-side state values.


Example

Suppose the Products page has:

Search = "Laptop"
Category = Electronics
Page = 2
Sort = Price Low → High

Angular can manage:

searchText =
  signal('Laptop');

selectedCategory =
  signal('Electronics');

page =
  signal(2);

sort =
  signal('priceAsc');

There is generally no reason to send every UI detail to ASP.NET Core for permanent storage.


Server-Side State

ASP.NET Core and its persistence layer should own authoritative business data.

Examples:

Users
Products
Product prices
Inventory
Orders
Payments
Permissions
Roles
Account status
Refresh-token/session records
Business rules

Usually:

Angular
   ↓
ASP.NET Core
   ↓
Database

The database is normally the durable source of truth for persistent business data.


Very Important Example: Product Price

Suppose SQL Server contains:

Laptop
Price = ₹75,000

Angular receives:

{
  "id": 1,
  "name": "Laptop",
  "price": 75000
}

Angular may temporarily hold:

product = signal<Product | null>(null);

But this does not mean Angular is the authoritative source.

A user can manipulate browser-side values.

For example, an attacker could attempt to change:

₹75,000

to:

₹1

Therefore checkout should not trust a client-submitted total such as:

{
  "productId": 1,
  "price": 1
}

as authoritative.

Instead, the client can send the required identifiers/quantities:

{
  "productId": 1,
  "quantity": 2
}

ASP.NET Core should retrieve authoritative pricing and apply business rules:

Angular
   ↓
ProductId = 1
Quantity = 2
   ↓
ASP.NET Core
   ↓
Retrieve authoritative price
   ↓
₹75,000
   ↓
Calculate
75,000 × 2
   ↓
₹150,000

Authorization Example

Angular may know:

currentUserRole =
  signal('Admin');

and use that information to decide whether to display:

Delete Product

button.

That's fine for UI behavior.

But Angular must not be the security authority.

Backend still requires something like:

[Authorize(Roles = "Admin")]
[HttpDelete("{id}")]
public IActionResult Delete(int id)
{
    // Delete...
}

Why?

Because:

Angular UI
    ↓
User controlled


ASP.NET Core
    ↓
Security boundary

A malicious user can bypass Angular and call:

DELETE /api/products/10

directly.


Client State Can Be a Copy of Server State

This distinction is important.

Products may exist:

SQL Server
   ↓
Authoritative Products

and Angular may also contain:

NgRx Store / Signal
   ↓
Products[]

That's not necessarily duplication in a bad sense.

Angular has a client-side representation/cache of server data.

SQL Server
Authoritative Data
      ↓
ASP.NET Core
      ↓
HTTP
      ↓
Angular
      ↓
Client-side representation

For example:

products = signal<Product[]>([]);

is perfectly fine.

But the server/database remains authoritative for persistent business truth.


Example: Shopping Cart

Shopping cart design depends on requirements.

For a temporary anonymous cart:

Angular / browser storage

may be sufficient.

For a logged-in user who expects:

Add Laptop on desktop
       ↓
Open mobile
       ↓
Laptop still in cart

the cart should normally also be persisted server-side.

Then:

Angular
     ↓
Client representation

ASP.NET Core + DB
     ↓
Persistent authoritative cart

Example: Form State

Suppose a user is editing an article:

Title
Content
Category
Tags
Meta Description

Before clicking Save:

Angular Form
     ↓
Temporary state

After clicking Save:

Angular
   ↓
POST / PUT
   ↓
ASP.NET Core
   ↓
Validation
   ↓
Database

After successful persistence:

Database
   ↓
Persistent state

This is a useful distinction:

Before Save
    ↓
Client working state


After Save
    ↓
Server persistent state

Client vs Server State Table

State Angular ASP.NET Core / DB
Menu open/closed ✓  
Selected tab ✓  
Search text ✓  
Current page ✓  
Loading indicator ✓  
Temporary form values ✓  
API product list copy/cache ✓  
Product authoritative price   ✓
Inventory   ✓
Orders Client copy possible ✓
Payments Display/status copy ✓
User permissions for UI Copy possible ✓ authoritative
Authorization enforcement   ✓
Business rules Some UI assistance ✓ authoritative
Persistent user data Client copy possible ✓
Refresh-token/session record   ✓

What About localStorage?

localStorage is client-side persistence, not a trusted database.

Good candidates might include non-sensitive preferences such as:

Theme
Language preference
Some UI settings

depending on requirements.

Don't treat:

localStorage

as authoritative for:

Role
Permissions
Product prices
Account balance
Payment status
Security decisions

A user controls their browser environment and can modify client-side storage.


Where Do Signals, Services, NgRx and ASP.NET Core Fit?

A useful architecture is:

                 ANGULAR
                    │
     ┌──────────────┴──────────────┐
     │                             │
 Local UI State              Shared State
     │                             │
 Signals                    Service / NgRx
     │                             │
     └──────────────┬──────────────┘
                    │
                 HttpClient
                    │
                    ▼
            ASP.NET Core API
                    │
             Authentication
             Authorization
             Business Rules
             Validation
                    │
                    ▼
                Database
                    │
                    ▼
          Persistent Business
                State

Simple Decision Guide

Is the state needed only by one component?

YES
 ↓
Local Signal

Example:

isModalOpen

Is it shared by several components but relatively simple?

YES
 ↓
Service + Signals/RxJS

Example:

Cart count
Current UI user state

Are shared state interactions and async workflows complex enough to require stronger conventions?

YES
 ↓
Consider NgRx

Example:

Products
Orders
Customers
Permissions
Complex workflows

Is it persistent or security/business-critical truth?

YES
 ↓
ASP.NET Core + Database

Example:

Price
Order
Payment
Inventory
Permissions

Key Points

  • Service-based state is simple and works well for many small-to-medium state requirements.
  • NgRx provides more formal, predictable state management for complex shared state.
  • Application size alone doesn't determine whether NgRx is necessary.
  • Signals are reactive primitives; NgRx is a broader state-management ecosystem.
  • Signals and NgRx can coexist.
  • Keep transient UI state in Angular.
  • Angular can cache or represent server data locally.
  • Keep authoritative persistent business data on the server/database.
  • Never trust client-side state for security decisions.
  • ASP.NET Core must enforce authentication, authorization, validation, and business rules.
  • localStorage is not a trusted server-side database.

Interview Questions & Answers

1. Service-based state vs NgRx?

Service-based state using Signals/RxJS is simpler and suitable when shared state and transitions are relatively straightforward. NgRx is useful when shared state, asynchronous workflows, state transitions, debugging, and team coordination become sufficiently complex to benefit from a formal architecture.


2. Signals vs NgRx?

Signals are Angular reactive primitives for representing and reacting to values. NgRx is a broader state-management ecosystem providing structured patterns such as Store, Actions, Reducers, Selectors, and Effects.

They can be used together.


3. What state should remain in Angular?

Primarily UI and temporary working state:

Search
Filters
Selected tab
Loading
Form state
Menu state

Angular may also hold client-side copies/caches of server data.


4. What state should remain on ASP.NET Core/server?

Authoritative persistent and security-sensitive business state:

Products
Prices
Inventory
Orders
Payments
Permissions
Business rules

usually persisted in a database.


5. Can NgRx Store replace the database?

No.

NgRx manages client-side application state. SQL Server or another backend data store provides durable server-side persistence.

NgRx Store
    ≠
Database

6. Can Angular store the current user's role?

Yes, Angular can maintain role information for UI behavior, but ASP.NET Core must independently enforce authorization.

[Authorize(Roles = "Admin")]

Interview Quick Answer

Use Angular services with Signals/RxJS when shared state is relatively simple, and consider NgRx when shared state, transitions, and asynchronous workflows require stronger structure and traceability. Signals are reactive primitives, whereas NgRx is a broader state-management architecture. Angular should mainly manage UI/working state and client-side representations of server data, while ASP.NET Core and the database remain authoritative for persistent business data, security, permissions, pricing, orders, and other critical state.

← Back to Articles