Great article!
Seems like I've been following most practices even before reading it :D
However, naming effects like functions is new to me, but I really like it +1
When it comes to treating actions as events instead of commands, there is one thing which puzzles me. Imagine you have a global piece of state which e.g. keeps feature flags for a given user. Now, if I want to refresh (load once again) the feature flags data I need to update the effect within a feature flags module/library every time there is new trigger for it (e.g. SavePreferencesSuccess action) . Doesn't it break the FF modules/library encapsulation? IMO a more natural approach is to dispatch a generic LoadFeatureFlags action (which is a part of the public API of the FF module/lib) from the effect belonging to a given feature (e.g. preferences effect).
@markostanimirovic any thoughts on that? Isn't editing reducers and/or effects of a given encapsulated module against the open-closed rule? What about shared libraries via a private npm. I remember working on the project which used a shared library for handling authentication. The library added the auth chunk to the global store and e.g. provided a possibility to dispatch an action resulting in refreshing an access token. Following the advice on using events instead of command I would need to edit the source code of the library, since the source of action in an application using the library is not known beforehand.
Like any solution/principle, good action hygiene also has pros and cons. I agree that it can violate the open-closed principle. However, it brings many benefits in terms of maintaining and upgrading the applications that are explained in this article.
In the case of auth library that is shared via private registry, I'd not expose any actions for dispatching. It can use NgRx flow internally and expose actions that can be listened to, but its side effects/state updates could be triggered from the outside world by using service(s). The similar situation is when you use @ngrx/router-store - it exposes actions that can be listened to, but side effects/state changes (navigation for example) are handled using the Angular Router (service). So you'll keep auth library encapsulated and still be able to apply good action hygiene in applications that use it.
Thanks for your reply.
In the case of the auth library, what you proposed is just not using NgRx at all for parts which would require generic actions. When talking about @ngrx/router-state, based on interaction via the Router instance it dispatches generic actions (so against good action hygiene). I do see some benefits of favoring actions as events over actions as commands, but I think that there's a place for both (see here for details blog.nrwl.io/ngrx-patterns-and-tec...).
Since, you seem to be a big fan of the actions as events approach I still have some questions in my mind, so let me ask them:
1) If you dispatch an action which is conditionally mapped to another action in an effect, what will be the source of an action in square brackets? The same as for the source action or sth else indicating it's from an effect?
2) Consider a single Angular app and a store chunk responsible for displaying notifications. It's an app wide module so it can be considered as a core one. Actions triggering notifications can be dispatched e.g. from Cars and Planes pages. Where would you define a file with CarsPage and PlanesPage actions containing actions responsible for triggering notifications? Under a feature module directory (then the Notifications module under core directory imports from the CarsPage/PlanesPage actions, since it needs to handle these actions in the notifications reducer) or under Notifications module directory (then a corresponding feature module imports the actions from there).
3) When using the Nx tool (even for a single app), the Notifications would be a shared library. Shared libs cannot access libs with a different scope by default, hence defining actions as a part of e.g. Cars data-access lib is not an option. Would you create an exception for the rule or define the actions as a part of the shared Notifications lib?
1) If you dispatch an action which is mapped (conditionally or not) to another action you are not dispatching unique events, but commands (see "Don't create boiler effects" section).
2) With good action hygiene, you describe an event, not command (what particular action should trigger). Therefore, all actions (events) from "Cars Page" (or "Planes Page") should be in the same file (see "Group actions by source" section). Single action can trigger multiple effects/reducers.
2) 3) Take a look at this article: github.com/ngrx/platform/discussio.... With this approach, you can apply good action hygiene and not break encapsulation. The same can be applied for auth library.
So you say that Victor Savkin is wrong in his article and there should only exist event actions, right?
1) I do not understand why such mapping is considered by you as a bad practice. For example, triggering data loading only when it's not in the store IMO is far more clear when you have an effect which conditionally dispatches LoadData action (command, I know).
2) Ok, so at the level of a feature module I keep actions related to e.g. Cars Page and I import them in every other feature/core module which need to be aware of a given action related to Cars Page, e.g. Cars Page Opened should trigger action which results in showing notification, so core Notifications module imports actions from a feature module. That sounds crazy. I had a discussion with @tomastrajan and he suggested to move such actions under core scope (tomastrajan.medium.com/level-up-yo...). However, it looks odd to keep under core scope actions like cars-api.actions such because e.g. I want to show a notification after API response.
3) It gives implicit solution which is a hacky way just to stick at all costs with some practices that simply do not fit all the scenarios.
I didn't say anybody was wrong. Don't do that, please.
My previous answers were about how I would handle scenarios that you described with good action hygiene applied because I thought you asked me exactly that:
Since, you seem to be a big fan of the actions as events approach I still have some questions in my mind, so let me ask them
Then you rephrased my answers the way you wanted them to sound, and said they were wrong or crazy, or even worse that I said someone else was wrong.
Keep using the commands if they work better in your projects. It's great that we have more options, so everyone can choose what suits them best!
Have a nice day, Wojciech! :)
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
Great article!
Seems like I've been following most practices even before reading it :D
However, naming effects like functions is new to me, but I really like it +1
When it comes to treating actions as events instead of commands, there is one thing which puzzles me. Imagine you have a global piece of state which e.g. keeps feature flags for a given user. Now, if I want to refresh (load once again) the feature flags data I need to update the effect within a feature flags module/library every time there is new trigger for it (e.g. SavePreferencesSuccess action) . Doesn't it break the FF modules/library encapsulation? IMO a more natural approach is to dispatch a generic LoadFeatureFlags action (which is a part of the public API of the FF module/lib) from the effect belonging to a given feature (e.g. preferences effect).
@markostanimirovic any thoughts on that? Isn't editing reducers and/or effects of a given encapsulated module against the open-closed rule? What about shared libraries via a private npm. I remember working on the project which used a shared library for handling authentication. The library added the auth chunk to the global store and e.g. provided a possibility to dispatch an action resulting in refreshing an access token. Following the advice on using events instead of command I would need to edit the source code of the library, since the source of action in an application using the library is not known beforehand.
Like any solution/principle, good action hygiene also has pros and cons. I agree that it can violate the open-closed principle. However, it brings many benefits in terms of maintaining and upgrading the applications that are explained in this article.
In the case of auth library that is shared via private registry, I'd not expose any actions for dispatching. It can use NgRx flow internally and expose actions that can be listened to, but its side effects/state updates could be triggered from the outside world by using service(s). The similar situation is when you use
@ngrx/router-store- it exposes actions that can be listened to, but side effects/state changes (navigation for example) are handled using the Angular Router (service). So you'll keep auth library encapsulated and still be able to apply good action hygiene in applications that use it.Thanks for your reply.
In the case of the auth library, what you proposed is just not using NgRx at all for parts which would require generic actions. When talking about @ngrx/router-state, based on interaction via the Router instance it dispatches generic actions (so against good action hygiene). I do see some benefits of favoring actions as events over actions as commands, but I think that there's a place for both (see here for details blog.nrwl.io/ngrx-patterns-and-tec...).
Since, you seem to be a big fan of the actions as events approach I still have some questions in my mind, so let me ask them:
1) If you dispatch an action which is conditionally mapped to another action in an effect, what will be the source of an action in square brackets? The same as for the source action or sth else indicating it's from an effect?
2) Consider a single Angular app and a store chunk responsible for displaying notifications. It's an app wide module so it can be considered as a core one. Actions triggering notifications can be dispatched e.g. from Cars and Planes pages. Where would you define a file with CarsPage and PlanesPage actions containing actions responsible for triggering notifications? Under a feature module directory (then the Notifications module under core directory imports from the CarsPage/PlanesPage actions, since it needs to handle these actions in the notifications reducer) or under Notifications module directory (then a corresponding feature module imports the actions from there).
3) When using the Nx tool (even for a single app), the Notifications would be a shared library. Shared libs cannot access libs with a different scope by default, hence defining actions as a part of e.g. Cars data-access lib is not an option. Would you create an exception for the rule or define the actions as a part of the shared Notifications lib?
1) If you dispatch an action which is mapped (conditionally or not) to another action you are not dispatching unique events, but commands (see "Don't create boiler effects" section).
2) With good action hygiene, you describe an event, not command (what particular action should trigger). Therefore, all actions (events) from "Cars Page" (or "Planes Page") should be in the same file (see "Group actions by source" section). Single action can trigger multiple effects/reducers.
2) 3) Take a look at this article: github.com/ngrx/platform/discussio.... With this approach, you can apply good action hygiene and not break encapsulation. The same can be applied for auth library.
So you say that Victor Savkin is wrong in his article and there should only exist event actions, right?
1) I do not understand why such mapping is considered by you as a bad practice. For example, triggering data loading only when it's not in the store IMO is far more clear when you have an effect which conditionally dispatches LoadData action (command, I know).
2) Ok, so at the level of a feature module I keep actions related to e.g. Cars Page and I import them in every other feature/core module which need to be aware of a given action related to Cars Page, e.g. Cars Page Opened should trigger action which results in showing notification, so core Notifications module imports actions from a feature module. That sounds crazy. I had a discussion with @tomastrajan and he suggested to move such actions under core scope (tomastrajan.medium.com/level-up-yo...). However, it looks odd to keep under core scope actions like cars-api.actions such because e.g. I want to show a notification after API response.
3) It gives implicit solution which is a hacky way just to stick at all costs with some practices that simply do not fit all the scenarios.
I didn't say anybody was wrong. Don't do that, please.
My previous answers were about how I would handle scenarios that you described with good action hygiene applied because I thought you asked me exactly that:
Then you rephrased my answers the way you wanted them to sound, and said they were wrong or crazy, or even worse that I said someone else was wrong.
Keep using the commands if they work better in your projects. It's great that we have more options, so everyone can choose what suits them best!
Have a nice day, Wojciech! :)