NgRx + Facades: Better State Management
Prior to Nx 6.2, Nx already provided scalable state management with NgRx.
Now there is a new option when generating NgRx files to also generate a facade on top of your state management… to help you work even better at-scale.
Facades are a programming pattern in which a simpler public interface is provided to mask a composition of internal, more-complex, component usages.
When writing a lot of NgRx code — as many enterprises do — developers quickly accumulate large collections of actions and selectors classes. These classes are used to [respectively] dispatch requests to- and query from- the NgRx Store.
Unfortunately — with this standard approach — each view component is required to know about many NgRx artifacts and about NgRx state management.
Using a Facade — to wrap and blackbox NgRx — simplifies accessing and modifying your NgRx state by masking internal all interactions with the Store
, actions
, reducers
, selectors
, and Effects
.
While this seems like a rather trivial change (and an extra layer), the Facade has a huge positive impact of developer productivity and yields significantly less complexity in the view layers.
Facades encourage developers to think in two (2) ways:
- Developers are encouraged to think about explicit public facade API(s).
- Developers start to think of views as presentational components that simply render data deliver from the Facade observables.
Remember these Facade observables are long-lived streams of data delivered based on queries to specific NgRx state.
Implementing our NgRx Facade
First, let’s take a look at how a Facade is implemented as a injectable service.
The facade has an explicit public API that exposes:
- public observables (e.g.
allCars$
) to private queries for Store state. - public methods (e.g.
selectCar()
) that hide internals of Store usages.
View Components + NgRx
Let’s take a look at why the Facade pattern is useful… by comparing a View Component implementation with and without facades.
CarListComponent without Facades:
The simple CarListComponent definition above ^ is required to import NgRx actions, selectors, and know how to use store.select()
, and store.dispatch()
. For more real world usages, this quickly gets complicated and messy.
CarListComponent with Facades
Developers will still leverage NgRx one-way data flows, immutable data structures, store.dispatch()
, store.select(),
effects for async processing, and more… but all this is hidden from the view layer.
From a view component’s point-of-view, NgRx Facades provide a more explicit public API and less moving parts.
Even better, Facades can inject and use other facades!
With Facades, writing state-dependent view components is much easier. To get hands-on with NgRx facades, check out this StackBlitz demo or use the Nx Schematic ngrx
to generate your own NgRx files!
Testing NgRx Facades
Using async/await techniques and mock services, developers can easily test the Facade’s public API.
Nx
ngrx
schematics will also generate a*.facade.spec.ts
class.
Consider the cars.facade.spec.ts
shown below:
In the above test class, we are asynchronously testing:
allCars$
properties by directly dispatching a `CarsLoaded` action and bypassing any similar functionality in thecars.effects.ts
.loadAll()
public method using a the standard `LoadCars` action dispatched to the NgRx store.- using the Nx utility method
readFirst
to read a single value from the target observable. - using
async
/await to
to ensure our testing and and the resulting output responses match an expected sequence. Note the try/catch used to delegate the error handling todone.fail(err).
Note, if the
cars.effects.ts
implementation internal used acars.http.service.ts
class, we could — in our test — register a mockup HttpService to simulate the handling aloadCars
action.
NgRx Facades with Nx Schematics
Use the latest Nx schematics to create both your NgRx generated files and an associated Facade implementation.:
# See help for the `ngrx` schematic
# Use the '-d' option to test in dryRun mode if desired.ng g ngrx -h# Generate ngrx files + facade in the 'cars' libraryng g ngrx cars --module=libs/cars/src/lib/cars.module.ts --facade -d
By default the facade classes are not generated with
ng g ngrx
; use thefacade
flag to include scaffolding for Facades.
Quick Links:
Special Thanks
I am so grateful for the amazing Angular CLI and the Nx power tools. I am also obligated to say thank you to:
- Jason Jean for his contributions to this article and for the StackBlitz demo.
- Joost Zöllner for his original illustration depicting view components coupled to NgrX artifacts.