/angular-dream-stack

Example of running multiple Routers and Ngrx Instances for Complex Dashboards with Webpack Module Federation

Primary LanguageTypeScriptMIT LicenseMIT

Netlify Status

Angular DREAM Stack

DOM-less Routing Engine Allowing Madness.

Demo

Dashboard

Motivation

This work was derived from a need for large enterprise internal facing applications to co-exist in a desktop like environment in the browser. The Angular Router was designed specifically for tying what is shown on the page to the location in the Browser. However, in some instances this can be behavior that is not desired. A good example of this is a dashboard with complex and rich widgets (charting, single metric, session tracing tools, etc). The default routing solution works really well until a widget such as a graph (with multiple routes) needs to be rendered multiple times on the page for different metrics.

Quick Start

Run yarn run-all to get all MFEs running,

Navigate to all below locally to see each application is able to open separately.

http://localhost:4001 - Counter

http://localhost:4002 - Scoreboard

http://localhost:4003 - Tour of Heroes

After verifying, navigate to http://localhost:4004 to play around with the Dashboard

The Solution

Sandboxing via Injector

Due to the power of Angular's Dependency Injection, we can create an isolated injector per widget. We can accomplish this by creating a custom service that provides code-splitting for each widget. This is accomplished by using the widget host component as the parent injector for each widget.

Allowing for Multiple Routers and Ngrx Stores

When we create our sandbox, we will also need to provide our own "override" for Router and Store and PlatformLocation. Overriding the PlatformLocation at the widget level is the key to allowing multiple routers to exist. The parent (root app router) will still have it's default behavior of deep linking, but the widgets will use an "in memory" url.

App Inception

Since each application has a sandboxed instance of the Router and Ngrx, we can have the application render a new instance of itself in itself.

Release the entire application as SPA and MFE App

Let's say we wanted to release the counter as a single page app but also release it in our MFE Dashboard. By taking what would be considered the AppModule and migrating it into a "library" will allow for the build process to consume the core functionality for two separate distribution styles.

  1. The AppModule is now responsible properly bootstrapping the application for SPA distribution. It's sole responsibility is NgModule that imports the CounterModule and has its own bootstrapped AppComponent that invokes the CounterContainerComponent.
  2. The MfeAppModule is responsible for creating a NgModule that implements the LoadableApp interface that provides the EntryComponent to be rendered in as a Federated Module. The MfeAppModule EntryComponent references the same CounterContainerComponent that was used for the SPA distribution, allowing for multiple distribution types of the same application.

Current Shortcomings

  • Hovering over a <a [routerLink]="...">...</a> will present a URL in the browser that will not work.
    • Should not be used with Angular Universal or any public facing site due to this.
    • Can become more supported by working with Angular team.
  • Functionality such as window.history.back() and window.history.forward() do not work since we essentially ripped out all communication with the browser.
  • To be successful with isolation with providers that hold state, refrain from using @Injectable({ providedIn: '...' }} syntax to avoid the compiler hoisting to an injector out of the sandbox.