nx icon indicating copy to clipboard operation
nx copied to clipboard

virtual router token injection omitted for mfe architecture

Open kenatwex opened this issue 2 years ago • 19 comments

SUBJECT: virtual router token injection omitted for mfe architecture

Current Behavior

this is about routing across the mfe spectrum and maintaining router context

Nx inspired manner to navigate across the mfe spectrum is to just use the router

  • '/host-my-app'
  • '/remote-cart'
  • '/remote-home'

But the host is required to initiate boot/load. But this host gobbling up all subsequent routes preventing navigation or normal angular router modeling (ex side-nav outlet, canvas outlet... when I click a mfe route link in side-nav it targets the canvas route by virtue of the angular template but host gobbles it up and I am unable to navigate)

Expected Behavior

A mechanism shold be implemented to virtualize mfe intended routers away from the isolated/separated (by nature) the browser dom. This was solved manually by Kyle Cannon in plain angular identifying this natural breach by angular alone

https://www.youtube.com/watch?v=fQrUGill9M0&ab_channel=ng-conf

https://github.com/kylecannon/angular-dream-stack

Nx should be guaranteeing our mfe routers 's are not victim to the gobbling up nature of plain angular or the browser

Steps to Reproduce

implement a side-nav with links and a right hand side canvas all pointing to mfe routes click link in side-nav the entire page gets loaded instead of punched into the canvas WHY? because the host router config (which is needed on load) gobbles up all subsequent routes

Are standard plain vanilla named outlets the way forward to solve this?

Thanks for an elite product and any advice forward

more forum gabb on this issue

Im finding I cannot just route anywhere across mfe's... my host is gobbling up mfe intended routes across the mfe spectrum... this issue appears to be addressed with plain angular manual implementation by Kyle Cannon https://www.youtube.com/watch?v=fQrUGill9M0&ab_channel=ng-conf by inducing virtual named outlets in order to synchronize a virtual token based router injection in order to virtualize the router per app/host/remote across the mfe spectrum contextualized and separated away from the browser dom... instead of a separated extrapolated isolated router injection anatomy across mfe's causing remotes to launch into their own isolated browser dom routing context destroying continuity across routers as they span across mfe's. Can someone from Nx team please attempt to address this? Has this been built into the Nx mfe architecture? Im finding Im heading into named outlets now but moreso the injected routers as well. Any feedback on this issue is greatly appreciated on what we need to do with the latest Nx release. My mfe monorepo is identical perfectly to the NX ng-module-federation example with regard to the routers and lazy loading entry modules and reachign into the shared libs and everything launches... But routing around the browser dom remain caught in nailing down the virtual router experience/implementation containing coherency as they span mfe's . Im wondering if this is already built into Nx and if not what are we to do? my host is gobbling up mfe intended routes across the mfe spectrum ... Thanks in advance

Environment

nx report

NX Report complete - copy this into the issue template

Node : 16.17.0 OS : darwin arm64 yarn : 1.22.18

nx : 14.6.4 @nrwl/angular : 14.6.4 @nrwl/cypress : 14.6.4 @nrwl/detox : Not Found @nrwl/devkit : 14.6.4 @nrwl/eslint-plugin-nx : 14.6.4 @nrwl/express : Not Found @nrwl/jest : 14.6.4 @nrwl/js : 14.6.4 @nrwl/linter : 14.6.4 @nrwl/nest : Not Found @nrwl/next : Not Found @nrwl/node : 14.6.4 @nrwl/nx-cloud : 14.6.1 @nrwl/nx-plugin : 14.6.4 @nrwl/react : Not Found @nrwl/react-native : Not Found @nrwl/schematics : Not Found @nrwl/storybook : 14.6.4 @nrwl/web : 14.6.4 @nrwl/workspace : 14.6.4 typescript : 4.7.4

Local workspace plugins:

Community plugins: @fortawesome/angular-fontawesome: 0.10.2 @ngrx/component-store: 14.2.0 @ngrx/effects: 14.2.0 @ngrx/entity: 14.2.0 @ngrx/router-store: 14.2.0 @ngrx/store: 14.2.0 apollo-angular: 4.0.1 @buildmotion/angular: 13.0.1 @ngrx/schematics: 14.2.0 @ngrx/store-devtools: 14.2.0 @storybook/angular: 6.5.10

kenatwex avatar Sep 09 '22 14:09 kenatwex

Can you provide a repository with what you have so far so I can take a look?

It also might help me understand the problem better. I'm not entirely sure what you mean by "gobbling up all subsequent routes".

To me it almost sounds like you want a child router-outlet that should load the contents of a side-nav page. This still feels like it could be solved using Angular primitives, but I need a repository of the problem to understand and investigate this issue.

Coly010 avatar Sep 13 '22 08:09 Coly010

Thanks Colum,

Im under way too much at this time to carve out a repo on this. My apologies. Things just got shutdown because of this lapse in functionality with mfe and Nx monorepo shelved at this time. I can attempt to resurrect but they want a solution to this asap.

Have you checked out Kyle Cannon's video? I mean its fairly easy to see what he implemented. A virtual dom coupled router obfuscator by a singleton root token injected router thats virtual across all MFE's. My sidenav is precisely how you stated with an embedded router-outler but since the browser and dom are dominating the routing (app/host/remote & its router) the host router is always the one who started routing on boot and subsequent routes thru the routing have to go back to the host for any/every remote.

I had hoped Nx implemented this mechanism via a static mfe vs dynamic mfe (if such a thing exists maybe I need to check nxconsole and check)(which would imply static router token injected or dynamic browser dom router)

Anyway heres a blip of what I got

HOST (app.routing.module.ts)

const routes: Routes = [
  {
    path: '',
    redirectTo: '/remote-auth',
    pathMatch: 'full',
  },
  {
    path: 'remote-auth',
    loadChildren: () =>
      loadRemoteModule('remote-auth', './Module').then(
        (m) => m.RemoteEntryModule
      ),
  },
  {
    path: 'remote-nav',
    canActivate: [AuthenticatedGuard],
    loadChildren: () =>
      loadRemoteModule('remote-nav', './Module').then(
        (m) => m.RemoteEntryModule
      ),
  }
}

Login module loads fine I authenticate and control yeilds back to the host because login module routed (as you advised) using this cmd

this.router.navigate(['/remote-nav']);

so that route takes control back to the host code above shows where this route causes the host to navigate to which mfe and lazy load and its the NAV

Nav module loads fine but it does not get projected into the correct why?

because this is the host template <router-outlet></router-outlet>

and so my nav template, this is the child router-outlet that should load the contents of a side-nav page see below the thats the intended target but the host is dictating the routing as shown above in its app.routing.module.ts by intercepting the route and gobbling up all mfe routes

    <div id="shell-template">
      <div id="navbar">
        <wexinc-side-nav
          [navTitle]="navTitle"
          [navMainIcon]="navMainIcon"
          [navItems]="navItems"
        >
          <ng-content navTitle select="[navTitle]"></ng-content>
          <ng-content navSmallTitle select="[navSmallTitle]"></ng-content>
        </wexinc-side-nav>
      </div>
      <div id="content">
        <router-outlet></router-outlet>
      </div>
    </div>

and its never get reached. The host gobbled up the remote-nav component projection. ANd the mfe never gets punched into the outlet id=content few lines above there.

this obviates the need for virtual token injected routers to obfuscate the browser dom associated to each app/router no?

watch the video it really makes sense

and I need to know if Nx team implemented this mechanism or now

or what Im suppose to do at this point

Thank you

kenatwex avatar Sep 14 '22 11:09 kenatwex

To me it almost sounds like you want a child router-outlet that should load the contents of a side-nav page.

I already have that Colum, see above. The MFE is a lazy loaded child route targeting the next router-outlet to encounter which now appears HAS TO BE GOVERNED by the host? Yes? and thats how its getting gobbled.

kenatwex avatar Sep 14 '22 11:09 kenatwex

The only thing I can spot inside nxconsole that might be something but I think im wrong

@nrwl/angular:host

is this parameter below

dynamic
boolean
Default: false
Should the host application use dynamic federation?

and I think this only has to do with locating the actual module of the mfe and nothing to do with token based virtual routing across all MFE's

so this is a major show stopper for us

nothing offered inside @nrwl/angular:remote either

any help is greatly appreciated.

kenatwex avatar Sep 14 '22 12:09 kenatwex

I need to see your NavigationModule

We view Federated apps as one single app, so there should only be one router. It's set up as a singleton across all places. So if your child routing setup has been done correctly, i.e. your remote app is using a router outlet with named router-outlet, then it should work fine

Coly010 avatar Sep 14 '22 12:09 Coly010

Okay I will provide that... but this discussion has a repo asking for this very thing too just for reference thanks Colum https://github.com/nrwl/nx/issues/7268

kenatwex avatar Sep 14 '22 12:09 kenatwex

Here is the app.module for remote named /remote-nav

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,

    RouterModule.forRoot(
      [
        {
          path: '',
          canActivate: [AuthenticatedGuard, RoleGuard],
          loadChildren: () =>
            import('./remote-entry/entry.module').then(
              (m) => m.RemoteEntryModule
            ),
        },
      ],
      { initialNavigation: 'enabledBlocking' }
    ),
  ],
  providers: [AdminGuard, RoleGuard, AuthenticatedGuard],
  bootstrap: [AppComponent],
})
export class AppModule {}

kenatwex avatar Sep 14 '22 12:09 kenatwex

and here is his entrymodule


@NgModule({
  declarations: [],
  imports: [
    CommonModule,
    RouterModule.forChild([
      {
        path: '',
        canActivate: [AuthenticatedGuard],
        loadChildren: () =>
          import('@wexinc/shared-root-ui-components').then(
            (m) => m.ComponentsModule
          ),
      },
    ]),
  ],
  providers: [],
})
export class RemoteEntryModule {}

we are lazy loading shared modules inside libs who have routes themselves

kenatwex avatar Sep 14 '22 12:09 kenatwex

Okay I will provide that... but this discussion has a repo asking for this very thing too just for reference thanks Colum https://github.com/nrwl/nx/issues/7268

kenatwex avatar Sep 14 '22 13:09 kenatwex

We view Federated apps as one single app, so there should only be one router. It's set up as a singleton across all places. So if your child routing setup has been done correctly, i.e. your remote app is using a router outlet with named router-outlet, then it should work fine

Thanks Colum.... I appreciate your assertion but what Im seeing is the route is being gobbled up by the host Now the previous post you commented on back in 2021 and your solution was PATHED route configurations? Am I correct for saying that?

Well this is good news fed apps are one big single app and one router singleton thats promising.

My route is still being gobbled up though

kenatwex avatar Sep 14 '22 13:09 kenatwex

So the lazy loaded Navigation module should have an empty index route of single quotes as follows just like a lazy loaded feature module

path: '',

and your saying it might need to be

path: 'remote-nav',

?

kenatwex avatar Sep 14 '22 13:09 kenatwex

Our goal is to also be able to alter the url by hand and enter REST urls like

http://localhost:4200/user/1
http://localhost:4200/user/2
http://localhost:4200/user

kenatwex avatar Sep 14 '22 13:09 kenatwex

Okay so named outlets was one option I wanted to consider

Is there a convention to keep it in sync with the route hierarchy?

can I call the named outlet the name of the remote? Is that safe?

kenatwex avatar Sep 14 '22 13:09 kenatwex

@kenatwex Can you take a look at my repo, where I have tried to create an example based on what you've said, to determine what the key differences are?

This repo uses

Host -> Remote -> Lazy Loaded Side Nav -> Lazy loaded pages into portion beside Side Nav

https://github.com/Coly010/nx-ng-mf-route

Coly010 avatar Sep 14 '22 15:09 Coly010

Hi Colum,

Im rendering my sidenav and still not projecting yet due to this small issue Im knocking on the door of finishing this... but Im really stuck.

routers can project components, redirects, modules

but MFE's ?

here is my routeconfig for my UI module below

FACT: both attempts to load my dashboard (which should punch into the content pane right hand side) are below and yeild errors

QUESTION: Is it legal from the libs area to load MFE's from apps folder? I feel strange about it and had to define tsconfig paths to get those

How can I successfully load an MFE from libs folder UI.MODULE.ts

LIBS/UI.MODULE.TS

const routes: Routes = [
  {
    path: '',
    component: NavigationComponent,
    children: [
      {
        path: 'remote-nav/remote-dashboard',
        outlet: 'content',
        canActivate: [AuthenticatedGuard],
        // loadChildren: () =>
        //   import('@wexinc/remote-dashboard/app/remote-entry/entry.module').then((m) => m.RemoteEntryModule),

        loadChildren: () =>
          loadRemoteModule('remote-dashboard', './Module').then(
            (m) => m.RemoteEntryModule
          ),
      }
  }
]
}

kenatwex avatar Sep 16 '22 21:09 kenatwex

crbug/1173575, non-JS module files deprecated. is the error from latter attempt above

the attempt commented out was yeilding rootDir errors complaining about the remoteEntryModule not living under the root folder of libs

kenatwex avatar Sep 16 '22 21:09 kenatwex

Hi COlum,

I should be able to do this from my libs/auth.module

  {
    path: 'remote-nav',
    canActivate: [AuthenticatedGuard],
    loadChildren: () =>
      loadRemoteModule('remote-nav', './Module')
        .then((m) => m.RemoteEntryModule)
  },

and im receiving this errors

Error: Uncaught (in promise): Error: Call setRemoteDefinitions or setRemoteUrlResolver to allow Dynamic Federation to find the remote apps correctly. Error: Call setRemoteDefinitions or setRemoteUrlResolver to allow Dynamic Federation to find the remote apps correctly.

I have all the remotes listed in my module.federation.config.js

module.exports = {
  name: 'host',
  remotes: [
    'remote-dashboard',
    'remote-nav',
    'remote-auth',
  ]
};

why isnt dynamic federations finding my remotes?

kenatwex avatar Sep 16 '22 22:09 kenatwex

You don't need dynamic federation in your case. You just need a standard import

Coly010 avatar Sep 19 '22 13:09 Coly010

Can MFE's survive router transclusion into any outlet positioned anywhere on a single page? Im wondering is Kyle Cannon's work needs attention in Nx. We are transcluding (projecting) entire MFE's into outlet's everywhere.

Is this legal with current Nx release on this MFE stuff or are there limitations?

Thank you

kenatwex avatar Sep 21 '22 01:09 kenatwex

This issue has been automatically marked as stale because it hasn't had any recent activity. It will be closed in 14 days if no further activity occurs. If we missed this issue please reply to keep it active. Thanks for being a part of the Nx community! 🙏

github-actions[bot] avatar Oct 06 '22 00:10 github-actions[bot]

This issue has been closed for more than 30 days. If this issue is still occuring, please open a new issue with more recent context.

github-actions[bot] avatar Mar 21 '23 11:03 github-actions[bot]