How to “inner join” data from multiple Stores on Angular

How to “inner join” data from multiple Stores on Angular

Angular ngrx stores

Update (03/06/2021):

You can find another more sophisticated approach to join different stores in the following post:


This is a powerful configuration based in a RootStore Module, and it let you create joins between stores without the need of reactive operators. Currently I use this architecture in my ngrx based implementations.


In this article we’ll cover a special scenario when working with Angular and Ngrx framework.

When you are dealing with complex software, Domain Driven Design is good approach to tackle this complexity. Breaking down of an extensive system into individual (sub)domains and their design is required in order to buid a scalable and mantainable system. The same design strategies that has been using for decades in the backend can be applied for building the frontend.

If you are developing the front of your application in Angular and you want to make use of these strategies, ngrx framework is going to help you. With Ngrx you can build reactive applications with Entity collection Management.

If you are building a large enterprise application, you will make use of Angular Modules to organize and structure the application. One useful strategy woud be to link each one of these domain models to its own Angular Feature Modules.

Redux is a very popular and elegant data architecture to build large applications. Redux data architecture explanation is out of the scope of this post, but you can find multiple sources of information about this topic in internet. One key idea of Redux is the store. All the data structure of the application (state of the application) is held in the store.

Furthermore if you build a hierachical entity data structure based in domain models, you will need to keep this data in separated stores, but it’s possible, that at some point, you will need to combine some of those data.


If you want to pull slices of store state, ngrx provides of group of funcions called “selectors”.1.

By design, when working with Ngrx and modules, you can attach a single slice of the store, called Feature to a single module.

The problem with ngrx comes when you try to combine data from diferent modules or features of you store, because you cannot combine “selectors” from different stores.

For this reason, I’m going to explain a strategy to pull and combine data from multiple feature ngrx Stores .

In this example, we have broken down our application structure design in two domain models, where each one is implemented within its own angular feature module.

In module A, we have an entity called Solution and in module B, we have an entity called Team. Even though these entites are part of different domains, there is an association link between both Solution and Team entities, since each Team is implementing a single solution at a certain point.

Application module design

Let’s say that at some point we want to display a list of Teams , and in each row we want to show the name of the solution.

For this endevour we will use an angular component, called teams-list-component.ts.

Each Team has the field solutionId which represents a “foreign key” to the solutions collection. 

That component will pull the data from two different feature stores, but because we are not in SQL world, we cannot do “inner join” queries. In order to get this done we can use rxjs operators like combineLatest.

The following code illustrates the part of the constructor where both stores are injected in the constructor of the component( teams & solutions):

    public store: Store

We have two selectors to retrieve all the data entities, each one from a different store.

Select all solution entities as array of objects:

export const selectSolutionsArray = createSelector(selectEntities, entities =>

Select all team entities as array of objects:

export const selectTeamsArray = createSelector(selectEntities, entities =>

In order to retrieve the data from the stores, the component needs to be subscribed to the different stores, using the previous “selectors”:

this.teams$ = this.store.pipe(select(selectTeamsArray));

this.solutions$ = this.store.pipe(select(selectSolutionsArray));

Since we are using different feature stores, we cannot build a “selector” to retrieve data based in a join between these two collections, something like:

export const selectorTeamJoinSolution = createSelector (
selectTeamsArray, selectSolutionsArray,
(teams: Team[], solutions: Solution[]) => {
 const TeamSolution = [];
      .....all combine stuff here......


Here’s where combineLatest comes into play.

If we want to display the data at the same time we will need to use a special rxjs operator called:


combineLatest is an rxjs operator that allows you to subscribe to multiple observables, and control when the data is emitted. What combineLatest does is wait until all of the observables emits data, and only then emit the combined data.

So this is exactly what we want to use . Let’s look how the code looks like now:

const teamJoinSolution = combineLatest([this.teams$, this.solutions$]).pipe(
      map(([teams, solutions]) =>
        teams.map(team => {
          return {
            solutionName: solutions.find(a => a.id === team.solutionId)?.name
 teamJoinSolution.subscribe(data => {

Take a look at the code. We pass both “queries collections” to combineLatest, and we filter the solutions using team foreing key id. After that we attach a new propery called “SolutionName” using a map and we return it to the data observable object.

The result is a inner join data set of teams with a new field called solutionName from “foreing key linked” Solution entity .


We covered how to display data and perform an “inner join” operation from multiple collections of separated stores in ngrx using combineLatest.

Hope you enjoyed the article!

  1. https://ngrx.io/guide/store/selectors []

Benötigen Sie Hilfe zu diesem Thema?

Ich werde Ihnen gerne weiterhelfen.

You can give me support if you find this content is usefull!

Buy Me a Coffee

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.