NX Angular monorepo and shared NgRx store
Tags: angular, nx, ngrx, architecture
Published: 2023-01-16 11:12:15

## THE ARCHITECTURAL GUIDE FOR SHARED STORE IN ANGULAR NX MONOREPO

In a project setting, where you have multiple Angular apps in a monorepo and some shared NgRx state, which these apps use, you would definitely want to **avoid duplicating the store code** in each app. For that you should create an NX library and **extract the reusable part of the store** there.

In this guide, I will demonstrate an example of 2 different apps: “Product Shop” and “Product Rent Agency”, which _share the same Products state_, therefore use the shared product store library and **extend it with their own actions, effects, reducers and selectors**.



The “Product Shop” and “Product Rent Agency” **both have to fetch all products**, so we need a FETCH **action, effect and reducer, and this is the reusable code, we want to extract to the shared store library.**

However, “Product Shop” can also SELL products, and “Product Rent Agency” can RENT products, which are completely different actions and require their own NgRx logic.




## NX store library architecture

We create an NX library for the reusable parts of the store and create a store in each app as well. Each concrete store is going to extend a shared store library.

> The shared library should not know any implementation details about concrete apps using it.

For that we are going to use **Injection Token** for an API URL, and **import shared store** parts in the concrete applications, but first let’s start with creating an NX library.

### Add a shared store library

```
nx g @nrwl/angular:ngrx --module=libs/shared-store/src/lib/shared-store.module.ts
```

### Add a store to each concrete app as well

Generate NgRx store in the NX Angular application, for example for our product-shop:

```
nx g @nrwl/angular:ngrx product-shop --root
```

Create a module for each app and register Effects and Store there, as we did in Product Shop application:

registering store for Product Shop app:
{% embed https://gist.github.com/dnlrbz/b6abc8119ea1381fb40957e0639bbb37 %}

registering store for Product Rent Agency app:
{% embed https://gist.github.com/dnlrbz/652a5436309624b056903b8b5ea1d945 %}

Note that each app should use the same `PRODUCTS_FEATURE_KEY`, so that we can **register Store with this key, and shared store actions are triggered from concrete applications**.

### Create an API token

This is important to isolate shared library from knowing the details about outside applications.

It still has to perform API calls with different URLs, but not know the details about Product Shop or Product Rent Agency applications. So we have to **provide an API TOKEN in concrete applications with its own API URL**

```
/**
 * necessary for generic effects, calling different API endpoints for each app
 */
export const API_TOKEN = new InjectionToken('apiToken');
```

`AppModule` of Product Rent Agency:

```
providers: [
    { provide: API_TOKEN, useValue: '/product-rent-agency/api' }
]
```

`AppModule` of Product Shop:

```
providers: [
    { provide: API_TOKEN, useValue: '/product-shop/api' }
]
```

### Actions

Each application has to fetch products, so we create **reusable product actions in the NX library**:

actions for fetching products:
{% embed https://gist.github.com/dnlrbz/ed269221aec8f81da4616c8c42ba76df %}

And “Product Shop” app has its own concrete actions to SELL products:
{% embed https://gist.github.com/dnlrbz/fd1f0768d509588f10d1de786c452dc2 %}

And “Product Rent Agency” can RENT products:
{% embed https://gist.github.com/dnlrbz/da27cb95435d80da25200729b34f3aa1 %}

### Effects

We are using `API_TOKEN` here so that each application can call its own different API URL, even while using shared `ProductsEffects`

Calling API to fetch products with an API_TOKEN:
{% embed https://gist.github.com/dnlrbz/470d12d20bfa7b64c486befd7ae55648 %}

We can define any logic in concrete Effects we want, for example in Product Rent Agency we Rent the product and call the respective API for that:

{% embed https://gist.github.com/dnlrbz/a6397d792b440df93240859a3de4b2e5 %}

In Product Shop we also might want to have some effects, for example to sell the product with an API call:

{% embed https://gist.github.com/dnlrbz/b15f83215792cacdb04c28be0b8bc5ca %}

### Reducers

Make sure to **export shared reducers as an array**, in order to import and reuse them later in concrete apps. In this example we export `productReducers`, which we are going to **import and use with help of spread operator** `...productReducers` in concrete reducers in Product Shop and Product Rent Agency.

Shared Product Reducers:
{% embed https://gist.github.com/dnlrbz/bf4ccac6bbdb9ed6214812cadc39556e %}

Reducers of Product Shop for updating its state when selling products, and imported productReducers with spread operator:

{% embed https://gist.github.com/dnlrbz/907d17172ca47bc191feb7524f977563 %}

Reducers of Product Rent Agency, and imported productReducers with spread operator:
{% embed https://gist.github.com/dnlrbz/c32cd56b5da3bee4264a29745724a7ca %}

### Selectors

There are reusable selectors which select the shared part of the state, as well as concrete selectors for concrete state parts. Both shared and concrete **use the same** `PRODUCTS_FEATURE_KEY` **to access the state**. This feature key is the only thing that is binding these selectors, so they are loosely coupled.

For example shared state has the `isLoading` flag, which we want to use in both apps, so let’s define a shared selector for that:

{% embed https://gist.github.com/dnlrbz/018cfb5f2bb502da7c0b7a1dac74c8e3 %}

And some selectors to select the concrete state of Product Rent Agency, let’s assume we want to select all Products and the number of rented items, which is part only of Product Rent Agency:

{% embed https://gist.github.com/dnlrbz/d6f521bcb6466790de6e27ed26ebbd67 %}

And for Product Shop we want to select revenue, which exists only in Product Shop:

{% embed https://gist.github.com/dnlrbz/a541e006ce58edcb506efdd7c1b24e70 %}

## Summary

With this implementation we have reduced code duplication for the same NgRx logic, that both apps use, by extracting it into the NX library.

See the whole implementation of an NX shared store library and two different apps inside the NX monorepo in my GitHub:

{% embed https://github.com/dnlrbz/ngrx-in-monorepo %}