Extract Implementation Details of ngrx from an Angular Application with the Facade Pattern

Lukas Ruebbelke
InstructorLukas Ruebbelke

Share this video with your friends

Send Tweet
Published 4 years ago
Updated 4 years ago

Extracting away the implementation details of ngrx from your components using the facade pattern creates some interesting possibilities in terms of iteratively migrating an application to ngrx. By decoupling your components from ngrx completely, this also makes testing and collaboration a lot easier. In this lesson, we will finalize our application by creating a facade to sit in between our component and ngrx and hide all of the ngrx implementation details away from our component. This has an added benefit of reducing the number of pieces that we need to export in our state module's barrel roll by reducing our dependency down to a single facade.

Instructor: [00:00] In this lesson, I am going to show you how to use the façade pattern to abstract out the implementation details of NgRx from your component. If we look at the projects component, you'll notice that there is a lot of NgRx stuff inside of the component class.

[00:25] This is fundamentally OK, but there is a way to hide some of this or hide these implementation details into a façade so that if anything changes underneath in terms of your implementation detail, then your component is oblivious to those particular details.

[00:47] I was a bit resistant to this pattern at first, until I needed to migrate an existing application to use NgRx. I realized that if I use the façade pattern to stand in between the component and the state management mechanism, that I was able to go through and upgrade this in piecemeal.

[01:08] After using this once or twice to do a conversion on legacy code into NgRx, I am a pretty big fan of this. You'll also notice that we've had to export bits and pieces out of our barrel roll. With a façade, we only need to export the façade. Everything else is contained within the state module.

[01:33] Let's start to build this out. Inside of the projects façade, we're going to go ahead and start to build out the façade service. We'll go ahead and export our service class. We are going to decorate it with our metadata.

[01:55] From here, what we're pretty much going to do is mimic whatever we were doing inside of our component in the façade. This provides a nice way to pull out our queries on the store, as well as our commands back to the store.

[02:17] Again, one of the nice things with NgRx is this separation of command and query. Inside of our façade, we're going to define the properties or the state that we want to pull off of the store. We will define the queries to pull that state off. We will then move our dispatch action calls into standalone functions. We'll call those directly.

[02:44] Within our constructor, we're going to inject the store and give it an appropriate type. Within the constructor body, we're going to go ahead and hydrate the projects and currentProject observable stream.

[03:05] This is exactly how we did it in the component. Everything you see here is going to be something that we've already done. We are just moving it over. From the component level, it's only going to know about the façade.

[03:22] We will hydrate currentProject. Now that we have fulfilled our query obligation, we can then go ahead and build out our placeholders for the commands or the store dispatch calls. We're going to do getProjects. We'll also do a selectProject. We'll paste in updateProject and deleteProject.

[03:51] We'll go ahead and just clean these up. We just need to make sure that our imports are correct. There we go. From here, let's go into our top-level barrel roll. We just need to export the projects façade.

[04:15] We'll set up this export here. Once we do that, then we will be able to remove the bits and the pieces of the project state that we've had to expose for consumption in our projects feature. This a way to just encapsulate that, tuck that away.

[04:36] Within the projects component, instead of calling the store, we are going to instead inject the façade. Where there is a store.pipe and we're doing those selectors, those will get replaced by essentially mapping that to the appropriate properties on our façade class.

[05:04] We'll go into our methods here. Anywhere that we're calling this.store.dispatch, instead we're going to call the appropriate method on our façade. We can go through and start to clean this up.

[05:30] We'll go down here to the getProjects. Let's go ahead and call this.façade.getProjects. We'll call createProject on the façade and send in the project as the payload. We'll do the same for updateProject and then as well deleteProject.

[06:08] If we go to the top of the class, then we can see that we have all these imports that we no longer need. Let's go ahead and just clean that up. This just becomes a lot simpler to work with because everything is tucked behind the façade.

[06:30] Essentially, the façade class itself is just handling the queries and then the commands, as well as we were able to clean up the barrel roll and just expose the projects façade and none of the underlying implementation details.

[06:48] Now we have a component that you could pass in any façade. It has no idea that under the hood we are dealing with NgRx. This is how you use a façade pattern in NgRx within an Angular application.

Varghese Pallathu
Varghese Pallathu
~ 4 years ago

I really enjoyed watching the course.

I have a similar application that I'm experimenting with two lazily loaded modules. I can compare that to Customers and Projects. They are loaded separately by the application through it's own routes. As I understand, when a lazily loaded angular module is loaded and if it has a store in it, the store will be re-created. So the data will be lost.

How do I design the angular application with an ngrx store that have two modules like Customers and Projects which are loaded lazily but at the same time the store will live until the application is shut down.

I posted the question in stack overflow also at https://stackoverflow.com/questions/56100953/how-to-make-feature-module-state-cached-globally-from-many-lazy-loading-modules

I appreciate your feedback.

Lukas Ruebbelke
Lukas Ruebbelkeinstructor
~ 4 years ago

This is a good question! Two points that I want to make here...

  1. I always isolate application state (NgRx) into its own module and using an NX workspace, I can consume it across N number of applications. Basically, I never split state across modules but instead consolidate it into a single module that can be shared across features. This keeps your application layer very small and concise.
  2. If you use Store.forFeature instead of Store.forRoot, your store is not overwritten as you add in new features.
Jim
Jim
~ 4 years ago

Hi Lucas. I really like the facade approach to ngrx. I'm curious what your approach is to "good action hygiene". Would your facade have a specific method for each component that needs to dispatch the "same" action? Would the method take a "source" string for the originator of the dispatch? Feels like having a .dispatch() method would defeat the purpose of the facade. Thanks.

Lukas Ruebbelke
Lukas Ruebbelkeinstructor
~ 4 years ago

Hi Jim - I believe that this approach of component specific methods for dispatching the same action would product negligible (if any) benefit. Components should be extremely thin in that they consume just enough data for the templates to render and convert user events into actions. As a result, the surface area for a bug to be introduced by a component is significantly reduced. I have found in practice that I only care about what is in the action and more importantly where it is going. Hope that helps!

Jim
Jim
~ 4 years ago

Lucus, thanks very much for your reply. I concur with your opinion and feel more comfortable now that you have validated that facade design!

Varghese Pallathu
Varghese Pallathu
~ 4 years ago

Thank you Lukas. I currently use Store.forFeature in every feature module. I have also only one application which I hope will work on different devices - uses bootstrap to be reactive. Let me see how the application will perform and then move to a global module.