Overview
Rodando Passenger uses NgRx Signals for state management, providing a reactive and type-safe approach to managing application state. Stores are simple, signal-based containers that hold state and expose computed selectors.
Store Pattern
Each store follows a consistent pattern:
State Interface : Defines the shape of the state
Signal-based State : Uses Angular signals for reactivity
Computed Selectors : Derived state via computed()
Mutation Methods : Methods to update state
Injectable Service : Provided at root level
Available Stores
AuthStore Authentication state and token management
TripPlannerStore Trip planning and route calculation state
SessionsStore Session lifecycle and device tracking
UsersStore User profiles and passenger information
AuthStore
Manages authentication state including tokens, user profile, and session information.
State Interface
src/app/store/auth/auth.store.ts
export interface AuthState {
accessToken : string | null ;
refreshTokenInMemory : string | null ;
user : UserProfile | null ;
loading : boolean ;
error : any | null ;
sessionType ?: SessionType | null ;
usesCookie ?: boolean | null ;
accessTokenExpiresAt ?: number | null ;
refreshTokenExpiresAt ?: number | null ;
sid ?: string | null ;
refreshInProgress ?: boolean ;
}
Computed Selectors
readonly accessToken = computed (() => this . _state (). accessToken );
readonly user = computed (() => this . _state (). user );
readonly loading = computed (() => this . _state (). loading );
readonly isAuthenticated = computed (() => {
const token = this . _state (). accessToken ;
const user = this . _state (). user ;
const exp = this . _state (). accessTokenExpiresAt ?? 0 ;
return !! token && !! user && exp > Date . now ();
});
readonly accessTokenExpiresIn = computed (() => {
const at = this . _state (). accessTokenExpiresAt ;
if ( ! at ) return null ;
return Math . max ( 0 , at - Date . now ());
});
Mutation Methods
setAccessToken
(token: string | null) => void
Updates the access token in state
setUser
(user: UserProfile | null) => void
Updates the user profile
setAuth
(payload: Partial<AuthState>) => void
Atomically updates multiple auth fields
Resets state to initial values
Usage Example
import { inject } from '@angular/core' ;
import { AuthStore } from './store/auth/auth.store' ;
export class MyComponent {
private authStore = inject ( AuthStore );
// Access computed signals
isAuthenticated = this . authStore . isAuthenticated ();
user = this . authStore . user ();
expiresIn = this . authStore . accessTokenExpiresIn ();
// Update state
logout () {
this . authStore . clear ();
}
}
TripPlannerStore
Manages trip planning state including origin, destination, route calculations, and fare estimates.
State Interface
src/app/store/trips/trip-planner.store.ts
export interface TripPlannerState {
originPoint : LatLng | null ;
originText : string | null ;
destinationPoint : LatLng | null ;
destinationText : string | null ;
routeSummary : RouteSummary | null ;
selectedVehicleCategoryId : string | null ;
selectedServiceClassId : string | null ;
fareQuote : FareQuote | null ;
loading : boolean ;
error : any | null ;
}
Key Selectors
readonly originPoint = computed (() => this . _state (). originPoint );
readonly destinationPoint = computed (() => this . _state (). destinationPoint );
readonly routeSummary = computed (() => this . _state (). routeSummary );
readonly fareQuote = computed (() => this . _state (). fareQuote );
readonly isReadyToRequest = computed (() => {
const s = this . _state ();
return !! ( s . originPoint && s . destinationPoint && s . routeSummary && s . fareQuote );
});
Mutation Methods
setOrigin
(point: LatLng, text?: string) => void
Sets the trip origin location
setDestination
(point: LatLng, text?: string) => void
Sets the trip destination location
setRoute
(route: RouteSummary) => void
Updates the calculated route
setFareQuote
(quote: FareQuote) => void
Sets the fare estimate
Clears all trip planning state
SessionsStore
Tracks active sessions and device information.
State Interface
export interface SessionsState {
sessions : Session [];
currentSession : Session | null ;
loading : boolean ;
error : any | null ;
}
Manages user profiles and passenger-specific data.
State Interface
export interface UsersState {
currentUser : User | null ;
passengers : Record < string , Passenger >;
loading : boolean ;
error : any | null ;
}
Best Practices
Signal-Based Reactivity : Stores use Angular signals which automatically track dependencies and trigger updates when state changes.
Computed Selectors : Use computed selectors to derive state. They are memoized and only recalculate when dependencies change.
Direct State Access : Never mutate _state directly. Always use provided mutation methods to ensure proper signal updates.
Store Communication
Stores are independent but can be composed:
export class MyFacade {
private authStore = inject ( AuthStore );
private tripStore = inject ( TripPlannerStore );
// Combine signals from multiple stores
canRequestTrip = computed (() => {
return this . authStore . isAuthenticated () &&
this . tripStore . isReadyToRequest ();
});
}
Facades Business logic layer that orchestrates stores and services
Effects Side effect management with NgRx Effects
State Management Overall state management architecture