What are benefits of using store (ngrx) in angular 2

I'm working on a angular 1.x.x project and thinking about upgrading my code to angular 2.

Now in my project I have many services(factory) for handling data which almost keep data in js arrays(both cache and storage) and process these data by using underscore for handling arrays.

I found that many examples in angular2 using ngrx.

What are benefits of using store compare to use data services to handle data?

Do I need multiple store for my app if I have multiple data type (stock, order, customer...)?

How can I structure (design) my app to deal with multiple data types like these?


Solution 1:

Even though your question is primarily opinion-based, I can give you some ideas why ngrx is a good choice. Despite people saying that it's not a good idea to have all of your application state in one single object (Single State Tree). However, in my opinion your state will be there regardless. With a store it's just all in once spot and mutations are explicit and tracked vs littered throughout and maintained locally by components. Additionally, you select specific properties from your store within your application, so you can only select data that you care about. If you then embrace immutability in your reducers by always returning a array for instance and use Observables, you can make use of the ChangeDetectionStrategy OnPush. OnPush gives you a nice performance boost. Let's take a look at the following figure taken from the official Angular docs:

enter image description here

As you can see, an Angular App is built using a component architecture, which results in a component tree. OnPush on a component means that only if input attributes change, the change detection will kick in. For example if Child B is OnPush and Child A is Default and you change something inside Child A, Child B's change detector won't be triggered since no input attributes have changed. However, if you change something inside Child B, Child A will be re-rendered since it has the default change detector.

So much about performance and the single state tree. Another advantage about the store is that you can actually reason about your code and state changes. So the reality of most Angular 1.x apps is scope soup. Here is a nice graphic from a blog post by Lukas Ruebbelke:

enter image description here

The picture demonstrates it pretty good. Another article from Tero Parviainen talks about how he improved his Angular apps by banning ng-controller. That all relates to the scope soup and managing ever-changing state is a difficult. The redux motivation says the following see here:

If a model can update another model, then a view can update a model, which updates another model, and this, in turn, might cause another view to update. At some point, you no longer understand what happens in your app as you have lost control over the when, why, and how of its state. When a system is opaque and non-deterministic, it’s hard to reproduce bugs or add new features.

By using ngrx/store you can actually get around this problem because you'll get a clear data flow in your app.

Since ngrx is highly inspired by redux, I would say that the same main principles apply:

  • Single source of truth
  • State is read-only
  • Changes are made with pure functions

So, in my opinion the biggest benefit is that you are able to easily track user interaction and reason about state changes because you dispatch actions and those lead always to one spot whereas with plain models you have to find all refernces and see what changes what and when.

Using ngrx/store also enables you to use devtools to see debug your state container and revert changes. Time travelling, I guess, was one of the main reasons for redux and that is pretty hard if you are using plain old models.

Testability as @muetzerich mentioned already is also a benefit of using ngrx/store. Reducers are pure functions and those functions are easy to test, because they take an input and simply return an output and do not depend on properties outside the function and have no side-effects, e.g. http calls etc.

To jump to the bottom line, I would say that don't need to use ngrx/store to do any of these things, but you will be tied to restrictions (the three principles mentioned above) which provide a common pattern and bring nice benefits.

To your questions:

Do I need multiple store for my app if I have multiple data type (stock, order, customer...)?

No, I would not suggest to use multiple stores.

How can I structure (design) my app to deal with multiple data types like these?

Maybe this blog post by Tero Parviainen helps you to to figure out how to design your store. He explains how to design the application state tree for an example app.

Solution 2:

A nice explanation about the benefis of using a store you can find in there documentation

Centralized, Immutable State

All relevant application state exists in one location. This makes it easier to track down problems, as a snapshot of state at the time of an error can provide important insight and make it easy to recreate issues. This also makes notoriously hard problems such as undo/redo trivial in the context of a Store application and enables powerful tooling.

Performance

Since state is centralized at the top of your application, data updates can flow down through your components relying on slices of store. Angular 2 is built to optimize on such a data-flow arrangement, and can disable change detection in cases where components rely on Observables which have not emitted new values. In an optimal store solution this will be the vast majority of your components.

Testability

All state updates are handled in reducers, which are pure functions. Pure functions are extremely simple to test, as it is simply input in, assert against output. This enables the testing of the most crucial aspects of your application without mocks, spies, or other tricks that can make testing both complex and error prone.

Multiple stores?

IMO use one store and add your data types as properties in your store.

Solution 3:

ngrx.store does what a well designed component/service will do, with added benefits. So far, and I'm early in working through how this comes together, this is what I've found:

It is really easy to get an unmaintainable mess with services and components. If you have cascading actions, complex data interactions and calls to remote locations you end up structuring a service that is almost identical to the action-reducer-store arrangement in ngrx. Small pure functions, observables, etc. ngrx has it already, why not use it and gain from the thought and patterns that it represents.

If forces/encourages thinking in small testable functions. To lay out a reducer or multiple reducers for a moderately complex component enforces a discipline that will remove so many of the hour consuming pitfalls. Nothing swallows hours like tracking down a quasi multithreaded race condition stemming from the callback queue. I'm sure this can happen with reducers, but it simplifies access to the call sequence and state for debugging.

The Angular2 pattern becomes easier. Templates with the display logic, components as a gathering place of all the bits and pieces that the template needs. Services become simpler as they simply do remote calls or handle the io for data from wherever it comes from. Then the actions and reducers for maintaining and changing the state, which triggers all the other parts to respond to the new state. I found that with component/service pattern either one would start getting big and complicated, with the side effect of it becoming extremely difficult to debug. My services were ending up storing the state and doing the data io.

Observables. Everything is an observable in rxjs.store, and that is the basis for a responsive app. Structuring app state as an observable is sometimes a bit arcane or not very straightforward, but figuring it out and doing it well pays large dividends further down the line.

The only negative that I can see is that the reducers become extraordinarily large very quickly. There seems to be lots of situations where the same code with different names is repeated, and repeated. My brain screams out 'function with parameters', but it doesn't work that way. On the other hand, this is where the structure and state of the app is expressed in all it's detail, so there is bound to be lots there. And when something goes wrong, as it inevitably will, having a pure function as the source of the problem makes it easier to track down and fix.