DEV Community

Hemant Singh
Hemant Singh

Posted on

From Monorepo to Polyrepo: Scaling Micro-Frontends Across Teams

MFE

From Monorepo to Polyrepo: Scaling Micro-Frontends Across Teams

Introduction

In my previous article, I explored building scalable applications with micro-frontends using a monorepo approach powered by Nx. While that approach works incredibly well for small-to-medium teams with shared ownership, I've since explored a polyrepo architecture that better addresses enterprise-scale challenges.

In this article, I'll share why I made the switch, the trade-offs involved, and real-world implementation details from a production deployment.


🎯 The Journey: From Monorepo to Polyrepo

Previous Approach: Monorepo

In the monorepo setup:

  • ✅ Everything lives in one repository
  • ✅ Nx manages dependencies and builds
  • ✅ Shared libraries are TypeScript path aliases
  • ✅ Easy local development
  • ✅ Atomic commits across multiple MFEs

Example Structure:

mfeworld/                          # Single repository
├── apps/
│   ├── dashboard/                 # Host application
│   ├── products/                  # Remote MFE
│   ├── cart/                      # Remote MFE
│   ├── orders/                    # Remote MFE
│   └── profile/                   # Remote MFE
├── libs/
│   └── shared/
│       ├── layout/               # Shared components
│       ├── services/             # Shared services
│       └── utils/                # Shared utilities
└── nx.json
Enter fullscreen mode Exit fullscreen mode

This worked great for:

  • Small teams (< 20 developers)
  • Shared ownership model
  • Rapid prototyping
  • Consistent tooling and dependencies

New Approach: Polyrepo

Now, each micro-frontend and the shared UI Kit live in separate repositories:

📦 Host Repository
├── https://github.com/hemantajax/mfe-host

📦 Shared UI Kit Repository
├── https://github.com/hemantajax/mfe-uikit
└── Published as: @hemantajax/mfe-uikit

📦 Remote MFE Repositories (8 separate repos)
├── products/      → https://hemantajax.github.io/mfedemos/products/
├── cart/          → https://hemantajax.github.io/mfedemos/cart/
├── profile/       → https://hemantajax.github.io/mfedemos/profile/
├── orders/        → https://hemantajax.github.io/mfedemos/orders/
├── analytics/     → https://hemantajax.github.io/mfedemos/analytics/
├── notifications/ → https://hemantajax.github.io/mfedemos/notifications/
├── messages/      → https://hemantajax.github.io/mfedemos/messages/
└── admin/         → https://hemantajax.github.io/mfedemos/admin/
Enter fullscreen mode Exit fullscreen mode

🔗 Live Demo - See it in action!


🤔 Why Polyrepo? The Business Case

1. Team Autonomy & Ownership 🎯

Problem in Monorepo:

  • All teams share the same repository
  • PR bottlenecks when multiple teams work simultaneously
  • Difficult to enforce team boundaries
  • Shared CI/CD pipeline can block deployments

Polyrepo Solution:

# Products team owns their repo completely
products/
  ├── .github/workflows/       # Their own CI/CD
  ├── src/                     # Their code
  └── package.json             # Their dependencies

# Cart team owns their repo
cart/
  ├── .github/workflows/       # Independent CI/CD
  ├── src/                     # Their code
  └── package.json             # Their dependencies
Enter fullscreen mode Exit fullscreen mode

Each team can:

  • ✅ Set their own release schedule
  • ✅ Choose their own tooling (within agreed standards)
  • ✅ Deploy independently
  • ✅ Control their own PR process

2. Independent Deployment Pipeline 🚀

Monorepo Challenge:

# Single pipeline for all apps
build-all:
  - Build: dashboard, products, cart, orders... (15+ minutes)
  - Test: All apps
  - Deploy: If ANY app fails, ALL deployments block
Enter fullscreen mode Exit fullscreen mode

Polyrepo Advantage:

# Products team deploys independently
products-pipeline:
  - Build: products only (3 minutes)
  - Test: products only
  - Deploy: products to CDN
  - ✅ No impact on other teams

# Cart team deploys at the same time
cart-pipeline:
  - Build: cart only (2 minutes)
  - Test: cart only
  - Deploy: cart to CDN
  - ✅ Completely independent
Enter fullscreen mode Exit fullscreen mode

Real-world Impact:

  • Deployment frequency increased from 2-3 times/week to 10+ times/day
  • Build time per MFE: 2-5 minutes vs. 15-20 minutes for entire monorepo
  • Failed builds don't block other teams

3. Technology Flexibility 🛠️

While our current stack is Angular 18, polyrepo allows:

// Host: Angular 18
@Component({...})
export class HostApp {}

// Products MFE: Angular 18 + Latest Features
import { signal, computed } from '@angular/core';

// Cart MFE: Could use React (future flexibility)
// No impact on other MFEs as long as Module Federation contract is maintained

// Profile MFE: Could use Vue (if needed)
// Each MFE is truly independent
Enter fullscreen mode Exit fullscreen mode

Real Example:
One team wanted to experiment with Angular's latest signal-based forms (experimental), while others preferred stable APIs. In polyrepo, this is trivial—each team controls their own package.json.


4. Granular Access Control 🔐

Enterprise Requirements:

# Financial services company scenario
products/          # Public team (20 developers)
cart/              # Public team (15 developers)
admin/             # Restricted (5 senior developers only)
analytics/         # Data team only (GDPR/compliance)
Enter fullscreen mode Exit fullscreen mode

With separate repositories:

  • ✅ GitHub permissions per repo
  • ✅ Different security policies per MFE
  • ✅ Audit trail per team
  • ✅ Branch protection rules per team preference

5. Clearer Versioning & Dependencies 📦

Shared UI Kit Approach:

# Published as npm package
@hemantajax/mfe-uikit@1.2.3
Enter fullscreen mode Exit fullscreen mode
// Products MFE (package.json)
{
  "dependencies": {
    "@hemantajax/mfe-uikit": "^1.2.0"
  }
}

// Cart MFE (package.json) - Can use different version
{
  "dependencies": {
    "@hemantajax/mfe-uikit": "^1.1.0"  // Still on older version
  }
}
Enter fullscreen mode Exit fullscreen mode

Advantages:

  • ✅ Semantic versioning for shared code
  • ✅ Teams upgrade at their own pace
  • ✅ No "big bang" upgrades across all apps
  • ✅ Backward compatibility testing becomes explicit

⚖️ Trade-offs: The Honest Truth

Advantages of Polyrepo ✅

Aspect Monorepo Polyrepo
Team Autonomy Low - Shared repo High - Independent repos
Deployment Speed Slow - Build everything Fast - Build only what changed
Independent Deployments Difficult Easy
Technology Flexibility Constrained High
Access Control Repository-level only Per-MFE granular
CI/CD Pipeline Single, complex Multiple, simple
Version Management Implicit via monorepo Explicit via npm versions
Onboarding New Teams One repo to clone Multiple repos (more setup)
Team Scaling Difficult beyond 20-30 devs Easy to scale to 100+ devs

Disadvantages of Polyrepo ❌

1. More Complex Setup

Monorepo:

git clone mfeworld
npm install
nx serve dashboard  # Everything works
Enter fullscreen mode Exit fullscreen mode

Polyrepo:

# Clone multiple repositories
git clone mfe-host
git clone mfe-uikit

# Each MFE team clones their own repo
git clone products
git clone cart
...

# Install dependencies in each
cd mfe-host && npm install
cd ../products && npm install
cd ../cart && npm install
Enter fullscreen mode Exit fullscreen mode

Mitigation:

  • Use workspace management tools (meta, git submodules)
  • Provide setup scripts
  • Docker Compose for local development
# docker-compose.yml
services:
  host:
    build: ./mfe-host
    ports: ['4200:4200']
  products:
    build: ./products
    ports: ['4201:4201']
  cart:
    build: ./cart
    ports: ['4202:4202']
Enter fullscreen mode Exit fullscreen mode

2. Shared Code Management

Monorepo:

// Direct imports, no publishing needed
import { HeaderComponent } from '@nxmfe/shared/layout';
Enter fullscreen mode Exit fullscreen mode

Polyrepo:

// Must publish to npm/GitHub Packages
import { HeaderComponent } from '@hemantajax/mfe-uikit';

// Requires:
// 1. Build UI Kit
// 2. Publish to registry
// 3. Update version in consuming MFEs
// 4. npm install in each MFE
Enter fullscreen mode Exit fullscreen mode

Mitigation:

  • Automated publishing via GitHub Actions
  • Semantic versioning
  • Changelog automation
  • Consider using Renovate Bot for dependency updates
# .github/workflows/publish-uikit.yml
name: Publish UI Kit
on:
  push:
    branches: [main]
jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: nx build uikit
      - run: npm publish dist/libs/uikit
Enter fullscreen mode Exit fullscreen mode

3. Cross-Cutting Changes

Scenario: Update authentication logic across all MFEs

Monorepo:

# Single PR affects all apps
nx run-many --target=test --all    # Test everything
git commit -m "feat: update auth"  # One commit
Enter fullscreen mode Exit fullscreen mode

Polyrepo:

# 1. Update UI Kit
cd mfe-uikit
# Make changes to AuthService
git commit -m "feat: update auth"
npm version minor  # 1.2.0 → 1.3.0
git push

# 2. Update each MFE (8 separate PRs!)
cd products
npm update @hemantajax/mfe-uikit@latest
git commit -m "chore: update uikit"

cd cart
npm update @hemantajax/mfe-uikit@latest
git commit -m "chore: update uikit"
# ... repeat for 6 more MFEs
Enter fullscreen mode Exit fullscreen mode

Mitigation:

  • Automation scripts for bulk updates
  • Renovate Bot for automatic PR creation
  • Feature flags for gradual rollout
  • Backward compatible changes

4. Testing Integration

Monorepo:

# Easy to run integration tests
nx run-many --target=e2e --all
Enter fullscreen mode Exit fullscreen mode

Polyrepo:

  • Need separate integration test repository
  • Must coordinate MFE versions
  • More complex CI/CD orchestration

Solution:

# Separate integration-tests repository
integration-tests/
  ├── tests/
  │   ├── user-journey.spec.ts     # Test across MFEs
  │   └── checkout-flow.spec.ts
  └── playwright.config.ts

# Uses deployed versions
playwright.config.ts:
  use: {
    baseURL: 'https://hemantajax.github.io/mfe-host/'
  }
Enter fullscreen mode Exit fullscreen mode

5. Debugging Across Repositories

Monorepo:

  • Single IDE workspace
  • Easy to set breakpoints across apps
  • Cmd+Click to jump between files

Polyrepo:

  • Multiple IDE windows
  • Need to use npm link for local UI Kit development
  • Debugging shared code requires linking

Workflow:

# Develop UI Kit locally
cd mfe-uikit
npm link

# Link in consuming MFE
cd ../products
npm link @hemantajax/mfe-uikit

# Make changes, see updates in real-time
cd ../mfe-uikit
# Edit code → products automatically picks up changes
Enter fullscreen mode Exit fullscreen mode

🏗️ Architecture Deep Dive

Module Federation Configuration

The magic that makes it all work:

Host Application (mfe-host):

// module-federation.config.prod.ts
export default {
  name: 'shell',
  remotes: [
    ['products', 'https://hemantajax.github.io/mfedemos/products/remoteEntry.mjs'],
    ['cart', 'https://hemantajax.github.io/mfedemos/cart/remoteEntry.mjs'],
    ['profile', 'https://hemantajax.github.io/mfedemos/profile/remoteEntry.mjs'],
    ['orders', 'https://hemantajax.github.io/mfedemos/orders/remoteEntry.mjs'],
    ['analytics', 'https://hemantajax.github.io/mfedemos/analytics/remoteEntry.mjs'],
    ['notifications', 'https://hemantajax.github.io/mfedemos/notifications/remoteEntry.mjs'],
    ['messages', 'https://hemantajax.github.io/mfedemos/messages/remoteEntry.mjs'],
    ['admin', 'https://hemantajax.github.io/mfedemos/admin/remoteEntry.mjs'],
  ],
};
Enter fullscreen mode Exit fullscreen mode

Route Configuration:

// app.routes.ts
export const appRoutes: Route[] = [
  {
    path: 'products',
    loadChildren: () => import('products/Routes').then((m) => m.remoteRoutes),
  },
  {
    path: 'cart',
    loadChildren: () => import('cart/Routes').then((m) => m.remoteRoutes),
  },
  // ... other routes
];
Enter fullscreen mode Exit fullscreen mode

Shared UI Kit Strategy

Publishing Workflow:

  1. Single npm Package (@hemantajax/mfe-uikit)
  • Contains all shared components, services, utilities
  • Versioned independently
  • Published to GitHub Packages (or npm)
  1. Internal Organization (in UI Kit repo)
mfe-uikit/
├── libs/
│   ├── layout/          # Header, Footer, Sidebar
│   ├── components/      # Buttons, Forms, Cards
│   ├── services/        # API, Auth, Storage
│   ├── utils/           # Helper functions
│   ├── pipes/           # Custom pipes
│   ├── directives/      # Custom directives
│   └── uikit/           # ⭐ Meta package (re-exports all)
Enter fullscreen mode Exit fullscreen mode
  1. Usage in MFEs:
// Before (monorepo):
import { HeaderComponent } from '@nxmfe/shared/layout';
import { AuthService } from '@nxmfe/shared/services';

// After (polyrepo):
import { HeaderComponent, AuthService } from '@hemantajax/mfe-uikit';
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • ✅ Single version to manage
  • ✅ One npm install command
  • ✅ Simpler imports
  • ✅ Can still organize code internally

📊 Real-World Metrics

Before (Monorepo)

  • Repository size: 500MB (with .git)
  • Full build time: 18 minutes
  • CI pipeline: 25 minutes
  • Deployments: 2-3 times per week
  • PR review time: 2-3 days (bottleneck)
  • Teams blocked: Often

After (Polyrepo)

  • Repository size: 50-80MB per repo
  • MFE build time: 2-5 minutes
  • CI pipeline per MFE: 5-8 minutes
  • Deployments: 10+ times per day (per team)
  • PR review time: Same day
  • Teams blocked: Never

🚀 When to Choose Polyrepo?

Choose Polyrepo When:

You have multiple independent teams (3+ teams)

  • Each team owns specific features/domains
  • Teams want different release schedules
  • Teams are in different time zones

You need independent deployments

  • Can't afford monorepo build times
  • Need to deploy hotfixes to single MFE
  • Different apps have different SLAs

Enterprise requirements

  • Need granular access control
  • Compliance/security boundaries
  • Different teams have different tech stacks

Scaling beyond 20-30 developers

  • PR bottlenecks in shared repo
  • Difficult to coordinate releases
  • CI/CD pipeline too complex

Choose Monorepo When:

Small team (< 20 developers)

  • Everyone can review all PRs
  • Shared ownership model works
  • Coordinated releases are preferred

Rapid prototyping

  • Quick iteration across multiple apps
  • Frequent refactoring of shared code
  • Atomic commits across apps are valuable

Simple deployment

  • Can afford to build everything
  • Single deployment schedule works
  • Less infrastructure complexity

🛠️ Implementation Checklist

If you're migrating from monorepo to polyrepo:

Phase 1: Preparation

  • [ ] Identify team boundaries
  • [ ] Define shared code strategy
  • [ ] Set up package registry (npm, GitHub Packages)
  • [ ] Plan repository structure
  • [ ] Document architecture decisions

Phase 2: Extract Shared Code

  • [ ] Create UI Kit repository
  • [ ] Migrate shared libraries
  • [ ] Set up automated publishing
  • [ ] Test package installation
  • [ ] Document shared API

Phase 3: Split Micro-Frontends

  • [ ] Create repo for each MFE
  • [ ] Migrate code and dependencies
  • [ ] Set up individual CI/CD
  • [ ] Update Module Federation configs
  • [ ] Test remote loading

Phase 4: Update Host

  • [ ] Update remote URLs
  • [ ] Update production configs
  • [ ] Test integration
  • [ ] Set up monitoring
  • [ ] Document deployment process

Phase 5: Developer Experience

  • [ ] Create setup scripts
  • [ ] Write comprehensive docs
  • [ ] Set up local development workflow
  • [ ] Automate dependency updates
  • [ ] Train teams

🔧 Developer Experience Tips

1. Local Development Setup

Use npm link for UI Kit development:

# Terminal 1: UI Kit (watch mode)
cd mfe-uikit
npm run build:watch

# Terminal 2: Link UI Kit
cd mfe-uikit
npm link

# Terminal 3: Products MFE
cd products
npm link @hemantajax/mfe-uikit
npm start
Enter fullscreen mode Exit fullscreen mode

2. Automated Dependency Updates

Renovate Bot Configuration:

{
  "extends": ["config:base"],
  "packageRules": [
    {
      "matchPackagePatterns": ["@hemantajax/mfe-uikit"],
      "groupName": "UI Kit",
      "automerge": true,
      "automergeType": "pr"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

3. Integration Testing

Separate test repository:

// integration-tests/tests/checkout.spec.ts
import { test, expect } from '@playwright/test';

test('complete checkout flow', async ({ page }) => {
  // Navigate to host
  await page.goto('https://hemantajax.github.io/mfe-host/');

  // Interact with products MFE
  await page.goto('/products');
  await page.click('[data-testid="add-to-cart"]');

  // Interact with cart MFE
  await page.goto('/cart');
  await expect(page.locator('[data-testid="cart-item"]')).toBeVisible();

  // Interact with orders MFE
  await page.click('[data-testid="checkout"]');
  await page.goto('/orders');
  await expect(page.locator('[data-testid="order-success"]')).toBeVisible();
});
Enter fullscreen mode Exit fullscreen mode

📚 Resources

Live Demo & Source Code

Remote MFEs (Deployed)

Related Articles


🎯 Conclusion

The shift from monorepo to polyrepo wasn't just about technology—it was about empowering teams to work independently while maintaining a cohesive architecture.

Key Takeaways:

  1. Polyrepo isn't inherently better than monorepo—it's a trade-off based on team size and organizational needs

  2. Start with monorepo if you're a small team or prototyping—the simplicity is worth it

  3. Migrate to polyrepo when team boundaries become clear and independent deployments become critical

  4. Invest in tooling to mitigate polyrepo complexity (automation, scripts, docs)

  5. Module Federation makes polyrepo micro-frontends feasible—without it, this architecture would be painful


What Would I Do Differently?

Looking back, I would:

  1. Create the UI Kit earlier - Even in monorepo, a separate UI Kit would have helped with versioning

  2. Automate more - Set up Renovate Bot and deployment automation from day one

  3. Better documentation - Invest in comprehensive setup guides before splitting repos

  4. Integration tests first - Have strong integration test coverage before splitting


💬 Discussion

What's your experience with monorepo vs. polyrepo? Have you tried micro-frontends in your organization?

I'd love to hear about:

  • What challenges did you face?
  • What would you do differently?
  • Any tips for teams considering this architecture?

Drop a comment below! 👇


Follow me for more:


Tags: #microfrontends #angular #architecture #modulefederation #webpack #nx #monorepo #polyrepo #webdev #javascript #typescript #frontend


This article is based on real production experience migrating a medium-sized team from monorepo to polyrepo architecture. All code examples and demos are available in the linked repositories.

Top comments (0)