Example of running multiple Routers and Ngrx Instances for Complex Dashboards with Webpack Module Federation
DOM-less Routing Engine Allowing Madness.
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.
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
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.
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.
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.
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.
MfeAppModule
EntryComponent references the same CounterContainerComponent that was used for the SPA distribution, allowing for multiple distribution types of the same application.<a [routerLink]="...">...</a>
will present a URL in the browser that will not work.
window.history.back()
and window.history.forward()
do not work since we essentially ripped out all communication with the browser.@Injectable({ providedIn: '...' }}
syntax to avoid the compiler hoisting to an injector out of the sandbox.