Recently I refactored my whole app from NGRX to NGXS, and upon completion of this laborious 160 file refactor I breathed a sigh of relief. Why?not for the work I had completed but for the amount of efficiency I had freed within the system — the possibility that was made available.

I started with each entity, mapping NGRX actions and selectors to NGXS actions and selectors. This task was surprisingly easy once I got into the flow.

In this article I am going to explain some of the patterns I like to implement with NGXS, some of the observations I have made about the library, and hopefully convince you as to why…

You should use NGXS for Angular state management

File structure & Naming

The file structure outlined below has been working quite well for me, it involves separation of major state concerns into separate files, for example —

//inside a single state folder pertaining to pelicans /pelican

- index.ts

- pelican.actions.ts

- pelican.selectors.ts

- pelican.state.ts

- pelican-state.model.ts

We have now clearly defined and separated our actions, state and selectors. The nice thing about this structure is that it scales quite well, and allows us to ‘roll-up’ all of our state into a single const which we can then inject into our model.

// inside the store folder which contains a bunch of folders like the one outlined above /store

- index.ts

- /pelican

- /salmon

- /migration-route

- /lake

An example of the ‘roll-up’ pattern is seen below, where we import all state in our store root index.ts file as a const and then simply import that const into the module, as seen below.

After a number of iterations, I have found this to be a really nice way to structure and modularise the application state.

Use the Router plugin

Connecting the Angular router to the store is definitely a good idea. The main reason being is that you can access the route state any time you need to, from within the store lifecycle. This is completely independent of your components & services. This is great for things such as route data, which I will outline as an example below.

Now we can select slices of the route state, at anytime in the state or component lifecycle.

Use an entity manager

I 100% advocate for using an entity adapter to manage your state. At the time of writing this, there isn’t an official entity adapter for NGXS (I have been using this lib).

To see more on the topic of NG redux entities here is an in depth explanation I would recommend reading.

However essentially you can;

First, define an entity adapter with a type definition.

export const pelicanEntityAdapter = createEntityAdapter<Pelican>({});

Then, inside the action handlers we can adapt state through the entity adapter.

@Action(GetPelicans) postPatterns({ getState, patchState }: stateCtx) {

return this.patternService.postPatterns(createPatterns).pipe(

tap(pelicans => patchState(

pelicanEntityAdapter.upsertMany(pelicans, getState()))

)

);

}

The cool thing is that we can upsertOne, upsertMany, addOne, deleteMany, etc and more — brilliant abstractions with less boilerplate.

High & Low order state

Redux architecture is revolutionary in modern SPA architecture, and in my opinion MVC seems like an ancient relic of a phprevious era. However, this doesn’t mean that redux isn’t without its flaws.

MVC involves distributing logic (aka complexity) between your controllers (components & services), whilst redux involves localising logic as much as possible within state definitions. This means that redux state can become convoluted.

I like to split up my state into a high and low order, what this means is that things like auth, user and entities state is stored at a higher level than things like ui, forms and loading states.

Within the context of angular the high order state can be stored in the core.module whilst lower level state is generally lazy loaded via feature modules. This concept is expressed in the graphic below, where the list and active tab are stored as a lower order and the user and auth are stored at a higher level, its important to note that;

This concept is largely subjective and varies from case to case, the lower order entities in the example could easily be higher order.

High order and lower state can be composed together.

Selector composition

Redux is all about clear separation of concerns, therefore it is important to have well defined selectors. To have a clean selectors I would recommend where possible, sticking to the principles below.

As much as possible, ensure logic is handled in the selector, and not in the component (the component should only select a value and reflect that value in the template)

Cleanly compose state by only importing higher order state into lower order state, never the other way around (NG lazy modules should not import from the core module).

A pattern for for organising selectors which has worked quite well thus far, is to define selectors in a selector class, and then import them into the component. In this fashion selectors act like db queries, selecting slices of state and performing unions where needed. You can see an example of this below.

Hopefully I have been able to share some of the patterns I have picked up from using NGXS. In my opinion this is the best currently available state management library for Angular, due to the great community surrounding it and the well thought-out approach that focuses on simplicity and minimal boilerplate.