DEV Community

BAVITHRA GUPTHA
BAVITHRA GUPTHA

Posted on

Microfrontend Architecture in a Real-World Application Using Angular

πŸ“Œ By Bavithra Jagannatha Guptha
Senior Frontend Developer | Angular | Microfrontends | Accessibility Advocate

πŸ” Project Focus
As part of a large-scale modernization initiative, our team rebuilt a legacy System using a microfrontend architecture powered by Angular.
This approach enabled us to:

  • Modularize the application
  • Improve scalability
  • Accelerate feature delivery
  • Maintain high accessibility standards

πŸ—οΈ Project Context
βœ… Problem: Monolithic legacy app slowing development and deployment
βœ… Objective: Build a scalable, maintainable solution enabling team autonomy
βœ… Tech Stack: Angular, Webpack Module Federation, Azure DevOps, Docker

⚠️ Key Challenges
πŸ”— Tight coupling in the legacy architecture
🐌 Long deployment cycles
β›” Difficulty enabling parallel team contributions
πŸ“‰ Performance issues with large datasets

πŸ’‘ Implementation Highlights

1. Defining Standard Contracts Between Microfrontends
A core concept in microfrontend architecture is the contract between microfrontends (MFEs). Each MFE must expose specific APIs, ensuring communication between them is smooth.

Here’s an example of a shared contract for a ClaimsService that can be consumed by other MFEs:

// claims.service.ts - Shared Service in a Shared Library
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class ClaimsService {
  constructor(private http: HttpClient) {}

  getClaimsData() {
    return this.http.get('/api/claims');
  }

  submitClaim(claimData: any) {
    return this.http.post('/api/submit-claim', claimData);
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Shell Application Handling Routing and MFE Orchestration
In the shell application, we load the various MFEs dynamically. Webpack Module Federation allows us to expose and consume these MFEs in a modular way.

webpack.config.js in the Shell application:

javascript
Copy
Edit
module.exports = {
  name: 'shell',
  remotes: {
    auth: 'auth@http://localhost:4201/remoteEntry.js',
    dashboard: 'dashboard@http://localhost:4202/remoteEntry.js',
    claimsForm: 'claimsForm@http://localhost:4203/remoteEntry.js',
    claimsHistory: 'claimsHistory@http://localhost:4204/remoteEntry.js'
  },
  shared: {
    angular: { singleton: true }
  }
};
Enter fullscreen mode Exit fullscreen mode

This setup allows us to load microfrontends like auth, dashboard, etc., from different ports at runtime.

Routing Setup in Shell Application (app-routing.module.ts):


const routes: Routes = [
  { path: '', loadChildren: () => import('dashboard/Module').then(m => m.DashboardModule) },
  { path: 'login', loadChildren: () => import('auth/Module').then(m => m.AuthModule) },
  { path: 'claims-form', loadChildren: () => import('claimsForm/Module').then(m => m.ClaimsFormModule) },
  { path: 'claims-history', loadChildren: () => import('claimsHistory/Module').then(m => m.ClaimsHistoryModule) }
];
Enter fullscreen mode Exit fullscreen mode

This approach ensures that each MFE can independently handle its routing logic, while the shell application orchestrates them.

3. Implementing Full CI/CD Pipelines with Azure DevOps
We set up Azure DevOps pipelines to automate the build, test, and deployment processes. Here’s an example of a simplified azure-pipelines.yml for the Shell application:

yaml
Copy
Edit
trigger:
  branches:
    include:
      - main

pool:
  vmImage: 'ubuntu-latest'

steps:
- task: NodeTool@0
  inputs:
    versionSpec: '16.x'
  displayName: 'Install Node.js'

- task: Npm@1
  inputs:
    command: 'install'
  displayName: 'Install Dependencies'

- task: Npm@1
  inputs:
    command: 'run build -- --prod'
  displayName: 'Build Angular Project'

- task: AzureWebApp@1
  inputs:
    azureSubscription: 'Your-Azure-Subscription'
    appName: 'your-web-app-name'
    package: '$(Build.ArtifactStagingDirectory)/**/*.zip'
Enter fullscreen mode Exit fullscreen mode

This YAML file sets up a pipeline that installs Node.js, builds the Angular project, and deploys it to Azure.

4. Docker for Deployment
Each microfrontend and the shell application are containerized using Docker. Below is an example Dockerfile for the shell application:

Dockerfile
Copy
Edit
# Use official node image as base
FROM node:16

# Set working directory
WORKDIR /app

# Copy package.json and install dependencies
COPY package*.json ./
RUN npm install

# Copy the rest of the app code
COPY . .

# Expose the port
EXPOSE 4200

# Build the Angular app and serve
RUN npm run build --prod
CMD ["npx", "http-server", "dist"]
Enter fullscreen mode Exit fullscreen mode

This Dockerfile builds the Angular app in production mode and serves it using http-server to run in a container.

🎯 Outcomes & Benefits
βœ… Faster deployments: Reduced from weeks to days
βœ… Decoupled development: Independent team workflows
βœ… Boosted performance: Faster load and response times
βœ… Improved UX/UI: Streamlined and accessible design
βœ… Simplified maintenance: Cleaner, modular codebase

πŸ‘ Final Thoughts
Adopting a microfrontend architecture allowed us to reimagine how large-scale applications can be built and maintained. The shift empowered teams to move faster, scale safely, and focus on user experience.

πŸ’¬ Have you adopted microfrontends in your project? I’d love to hear your thoughts and lessons learned!
πŸ”— If you'd like a code walkthrough or more technical insights, feel free to connect or message me.

Top comments (0)

OSZAR »