05

Module 5: Angular Host and Remote Applications with Module Federation

Chapter 5 • Intermediate

30 min

Angular Host and Remote Applications with Module Federation

A Micro Frontend system consists of:

  • Host Application - Application shell, routing orchestration, layout and navigation
  • Remote Applications - Fully independent Angular apps that expose modules to the Host

Users only access the Host.


Host Application

Responsibilities

  • Application Shell - Main container, layout, navigation
  • Routing Orchestration - Routes to appropriate Remote apps
  • Shared Layout - Header, footer, sidebar
  • Global State - User authentication, theme, etc.

Characteristics

  • Minimal Business Logic - Mostly infrastructure
  • Rarely Changes - Stable foundation
  • Deploys Infrequently - Only when infrastructure changes
  • Entry Point - Users always start here

Example Structure

code
host-app/
├── src/
│   ├── app/
│   │   ├── shell/          # Application shell
│   │   ├── layout/         # Layout components
│   │   ├── routing/        # Route configuration
│   │   └── shared/         # Shared services
│   └── assets/
└── webpack.config.js       # Module Federation config

Remote Applications

Responsibilities

  • Business Domain Logic - Complete feature implementation
  • Domain-Specific UI - Components for the domain
  • Domain State - State management for the domain
  • Module Exposure - Expose modules to Host

Characteristics

  • Fully Independent - Can run standalone
  • Complete Applications - Not just components
  • Frequent Changes - Active development
  • Independent Deployment - Deploy on own schedule

Example Structure

code
products-app/
├── src/
│   ├── app/
│   │   ├── products/       # Products feature
│   │   ├── product-detail/ # Product detail
│   │   └── shared/         # Domain-specific shared
│   └── assets/
└── webpack.config.js       # Module Federation config

Host Application Architecture

Routing Configuration

typescript.js
// host-app/src/app/app-routing.module.ts
const routes: Routes = [
  {
    path: 'products',
    loadChildren: () =>
      import('products/ProductsModule').then(m => m.ProductsModule)
  },
  {
    path: 'checkout',
    loadChildren: () =>
      import('checkout/CheckoutModule').then(m => m.CheckoutModule)
  },
  {
    path: '',
    redirectTo: '/products',
    pathMatch: 'full'
  }
]

Shell Component

typescript.js
// host-app/src/app/shell/shell.component.ts
@Component({
  selector: 'app-shell',
  template: `
    <app-header></app-header>
    <main>
      <router-outlet></router-outlet>
    </main>
    <app-footer></app-footer>
  `
})
export class ShellComponent {}

Module Federation Config

typescript.js
// host-app/webpack.config.js
new ModuleFederationPlugin({
  name: 'host',
  remotes: {
    products: 'products@http://localhost:4201/remoteEntry.js',
    checkout: 'checkout@http://localhost:4202/remoteEntry.js'
  },
  shared: {
    '@angular/core': { singleton: true, strictVersion: true },
    '@angular/common': { singleton: true, strictVersion: true },
    '@angular/router': { singleton: true, strictVersion: true }
  }
})

Remote Application Architecture

Module Exposure

typescript.js
// products-app/src/app/products/products.module.ts
@NgModule({
  declarations: [ProductsComponent, ProductDetailComponent],
  imports: [CommonModule, RouterModule],
  exports: [ProductsComponent]
})
export class ProductsModule {}

Routing in Remote

typescript.js
// products-app/src/app/products/products-routing.module.ts
const routes: Routes = [
  { path: '', component: ProductsComponent },
  { path: ':id', component: ProductDetailComponent }
]

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class ProductsRoutingModule {}

Module Federation Config

typescript.js
// products-app/webpack.config.js
new ModuleFederationPlugin({
  name: 'products',
  filename: 'remoteEntry.js',
  exposes: {
    './ProductsModule': './src/app/products/products.module.ts'
  },
  shared: {
    '@angular/core': { singleton: true, strictVersion: true },
    '@angular/common': { singleton: true, strictVersion: true },
    '@angular/router': { singleton: true, strictVersion: true }
  }
})

Communication Patterns

1. Routing-Based Communication

  • Host routes to Remote
  • URL-based navigation
  • Standard Angular routing

2. Event-Based Communication

typescript.js
// Shared event bus
@Injectable({ providedIn: 'root' })
export class EventBus {
  private subject = new Subject<any>()
  
  emit(event: string, data: any) {
    this.subject.next({ event, data })
  }
  
  on(event: string): Observable<any> {
    return this.subject.pipe(
      filter(e => e.event === event),
      map(e => e.data)
    )
  }
}

3. Shared State Service

typescript.js
// Shared state service
@Injectable({ providedIn: 'root' })
export class SharedStateService {
  private userSubject = new BehaviorSubject<User | null>(null)
  user$ = this.userSubject.asObservable()
  
  setUser(user: User) {
    this.userSubject.next(user)
  }
}

Standalone Mode

Remote applications should work standalone:

Standalone Routing

typescript.js
// products-app/src/app/app-routing.module.ts
const routes: Routes = [
  { path: 'products', component: ProductsComponent },
  { path: 'products/:id', component: ProductDetailComponent }
]

Benefits

  • ✅ Development without Host
  • ✅ Testing in isolation
  • ✅ Independent deployment
  • ✅ Fallback if Host fails

Development Workflow

Local Development

  1. Start Host app: ng serve host-app --port 4200
  2. Start Products Remote: ng serve products-app --port 4201
  3. Start Checkout Remote: ng serve checkout-app --port 4202
  4. Access Host at http://localhost:4200

Independent Development

  • Remote apps can run standalone
  • Test features in isolation
  • No need to start all apps

Deployment Architecture

Host Deployment

  • Deploys to: https://app.example.com
  • Serves: Application shell
  • Rarely changes

Remote Deployments

  • Products: https://products.example.com
  • Checkout: https://checkout.example.com
  • Deploy independently
  • Host references via URLs

Best Practices

Host Application

  • ✅ Keep minimal and stable
  • ✅ Handle global concerns only
  • ✅ Provide clear routing structure
  • ✅ Manage shared dependencies

Remote Applications

  • ✅ Make them fully functional standalone
  • ✅ Expose clear module boundaries
  • ✅ Minimize dependencies on Host
  • ✅ Handle errors gracefully

The next module explains when to use this architecture.