The project demonstrates three different approaches to organizing a frontend application:
- Monolithic application - traditional approach with a single codebase
- Runtime micro-frontend integration - using Module Federation to load modules at runtime
- Build-time micro-frontend integration - bundling all modules together at build time
| Version | Link (development) | Link (production) |
|---|---|---|
| Ecommerce Monolith | http://localhost:3010/ | http://localhost:9001/ |
| Ecommerce MF Runtime Integration | http://localhost:3100/ | http://localhost:9002/ |
| Ecommerce MF Build Integration | http://localhost:3200/ | http://localhost:9003/ |
A traditional monolithic application where all components, sections, and logic are in a single codebase. The application is bundled as one bundle and deployed as a single unit.
- Single codebase: All modules (Product, Checkout, Inspire) are in one repository
- Single bundle: One JavaScript bundle is created during build
- Shared dependencies: React and other dependencies are loaded once
- Simple deployment: Only one application needs to be deployed
- Tight coupling: All components have direct access to each other
graph TB
subgraph "Ecommerce Monolith App"
Shell[Shell/App Component]
ProductSection[Product Section]
CheckoutSection[Checkout Section]
InspireSection[Inspire Section]
SharedContext[Shared Contexts<br/>Theme, Cart, Products]
end
subgraph "Dependencies"
React[React & React-DOM]
UICore[UI Core Components]
end
Shell --> ProductSection
Shell --> CheckoutSection
Shell --> InspireSection
Shell --> SharedContext
ProductSection --> SharedContext
CheckoutSection --> SharedContext
InspireSection --> SharedContext
ProductSection --> React
CheckoutSection --> React
InspireSection --> React
Shell --> React
ProductSection --> UICore
CheckoutSection --> UICore
InspireSection --> UICore
graph LR
A[Source Code] --> B[Webpack Build]
B --> C[Single Bundle<br/>main.js]
C --> D[Web Server<br/>Port 3010]
- ✅ Simple development and debugging
- ✅ No dependency versioning issues
- ✅ Bundle size optimization at the application level
- ✅ Easy state sharing between components
- ✅ Fast loading due to single bundle
- ❌ Requires deployment of entire application for changes in one section
- ❌ Large codebase can slow down development
- ❌ Resource monopolization by one team
- ❌ Difficulty scaling teams
apps/ecommerce-monolith/
├── src/
│ ├── App.js # Main application component
│ ├── sections/
│ │ ├── Product.js # Product section
│ │ ├── Checkout.js # Checkout section
│ │ └── Inspire.js # Inspire section
│ └── context/
│ ├── ThemeContext.js
│ ├── CartContext.js
│ └── ProductsContext.js
└── webpack.config.js # Build configuration
Micro-frontend architecture using Webpack Module Federation to load remote modules at runtime. Each micro-frontend (Product, Checkout, Inspire) is built and deployed independently, and the Shell application loads them dynamically through remoteEntry.js files.
- Independent applications: Each micro-frontend is a separate application with its own dev server
- Dynamic loading: Modules are loaded at runtime via HTTP
- Module Federation: Uses Webpack Module Federation Plugin
- Shared dependencies: React and other dependencies are loaded once and used by all micro-frontends
- Independent deployment: Each micro-frontend can be deployed separately
graph TB
subgraph "Browser"
ShellApp[Shell App<br/>Port 3100]
LazyLoad[React.lazy<br/>Dynamic Imports]
end
subgraph "Micro-frontends"
ProductMF[Product MF<br/>Port 3101]
CheckoutMF[Checkout MF<br/>Port 3102]
InspireMF[Inspire MF<br/>Port 3103]
end
subgraph "Module Federation"
ProductRemote[remoteEntry.js<br/>Product]
CheckoutRemote[remoteEntry.js<br/>Checkout]
InspireRemote[remoteEntry.js<br/>Inspire]
end
ShellApp --> LazyLoad
LazyLoad --> ProductRemote
LazyLoad --> CheckoutRemote
LazyLoad --> InspireRemote
ProductRemote -.HTTP.-> ProductMF
CheckoutRemote -.HTTP.-> CheckoutMF
InspireRemote -.HTTP.-> InspireMF
ShellApp --> SharedReact[Shared React]
ProductMF --> SharedReact
CheckoutMF --> SharedReact
InspireMF --> SharedReact
graph TB
subgraph "Build Phase"
A1[Shell Build] --> B1[Shell Bundle<br/>+ Remote Config]
A2[Product Build] --> B2[Product Bundle<br/>+ remoteEntry.js]
A3[Checkout Build] --> B3[Checkout Bundle<br/>+ remoteEntry.js]
A4[Inspire Build] --> B4[Inspire Bundle<br/>+ remoteEntry.js]
end
subgraph "Deployment Phase"
B1 --> D1[Web Server<br/>Port 3100]
B2 --> D2[Web Server<br/>Port 3101]
B3 --> D3[Web Server<br/>Port 3102]
B4 --> D4[Web Server<br/>Port 3103]
end
subgraph "Runtime Phase"
D1 --> Browser[Browser]
Browser -.HTTP Request.-> D2
Browser -.HTTP Request.-> D3
Browser -.HTTP Request.-> D4
end
sequenceDiagram
participant Browser
participant Shell as Shell App (3100)
participant Product as Product MF (3101)
participant Checkout as Checkout MF (3102)
participant Inspire as Inspire MF (3103)
Browser->>Shell: Load application
Shell->>Browser: Load Shell bundle
Browser->>Shell: Request Product section
Shell->>Product: Load remoteEntry.js
Product-->>Browser: Transfer Product/Section
Browser->>Shell: Request Checkout section
Shell->>Checkout: Load remoteEntry.js
Checkout-->>Browser: Transfer Checkout/Section
Browser->>Shell: Request Inspire section
Shell->>Inspire: Load remoteEntry.js
Inspire-->>Browser: Transfer Inspire/Section
Shell (host):
remotes: {
ecommerce_product: 'ecommerce_product@http://localhost:3101/remoteEntry.js',
ecommerce_checkout: 'ecommerce_checkout@http://localhost:3102/remoteEntry.js',
ecommerce_inspire: 'ecommerce_inspire@http://localhost:3103/remoteEntry.js',
}Remote (exposes):
exposes: {
'./Section': './src/Section'
}- ✅ Independent deployment of micro-frontends
- ✅ Isolated teams can work independently
- ✅ Flexibility in technology choice for each micro-frontend
- ✅ On-demand module loading (lazy loading)
- ✅ Ability to A/B test individual micro-frontends
- ❌ Difficulty debugging distributed modules
- ❌ Potential issues with dependency versions
- ❌ Additional HTTP requests for module loading
- ❌ Requires coordination between teams for shared APIs
- ❌ More complex CORS and security setup
apps/
├── ecommerce-mf-shell/ # Host application
│ ├── src/
│ │ └── bootstrap.js # Dynamic imports via React.lazy
│ └── webpack.config.js # Module Federation remotes
├── ecommerce-mf-product/ # Remote module
│ ├── src/
│ │ └── Section.js # Exported component
│ └── webpack.config.js # Module Federation exposes
├── ecommerce-mf-checkout/ # Remote module
└── ecommerce-mf-inspire/ # Remote module
A hybrid approach where micro-frontends are developed as separate packages in a monorepo but bundled together with the Shell application at build time. Uses regular npm/yarn package imports instead of Module Federation.
- Separate development: Each section is developed as a separate package
- Bundled together: All packages are bundled into one bundle at build time
- Regular imports: Uses standard ES6 imports instead of Module Federation
- Monorepo: All packages are in one repository (Turborepo)
- Single bundle: Build result is one JavaScript file
graph TB
subgraph "Monorepo Structure"
Shell[Shell App<br/>Port 3200]
subgraph "Packages"
ProductPkg["@packages/ecommerce-sections-product"]
CheckoutPkg["@packages/ecommerce-sections-checkout"]
InspirePkg["@packages/ecommerce-sections-inspire"]
UICore["@ui-core/web-components"]
end
end
subgraph "Build Process"
Webpack[Webpack Bundler]
SingleBundle[Single Bundle<br/>main.js]
end
Shell --> ProductPkg
Shell --> CheckoutPkg
Shell --> InspirePkg
ProductPkg --> UICore
CheckoutPkg --> UICore
InspirePkg --> UICore
Shell --> Webpack
ProductPkg --> Webpack
CheckoutPkg --> Webpack
InspirePkg --> Webpack
UICore --> Webpack
Webpack --> SingleBundle
graph TB
subgraph "Development Phase"
Dev1[Product Package<br/>Development]
Dev2[Checkout Package<br/>Development]
Dev3[Inspire Package<br/>Development]
end
subgraph "Build Phase"
A[Shell App] --> B[Turborepo Build]
Dev1 --> B
Dev2 --> B
Dev3 --> B
B --> C[Webpack Bundle]
C --> D[Single Bundle<br/>main.js]
end
subgraph "Deployment Phase"
D --> E[Web Server<br/>Port 3200]
E --> F[Browser]
end
graph LR
Shell[Shell App] --> Product[Product Package]
Shell --> Checkout[Checkout Package]
Shell --> Inspire[Inspire Package]
Product --> UICore[UI Core]
Checkout --> UICore
Inspire --> UICore
Shell --> React[React]
Product --> React
Checkout --> React
Inspire --> React
Shell application:
import ProductSection from '@packages/ecommerce-sections-product'
import CheckoutSection from '@packages/ecommerce-sections-checkout'
import InspireSection from '@packages/ecommerce-sections-inspire'- ✅ Separate development of sections as individual packages
- ✅ Single bundle without additional HTTP requests
- ✅ Simple debugging (one bundle, one source map)
- ✅ Bundle size optimization at build time
- ✅ Code isolation through packages, but unified build
- ❌ Requires rebuilding entire application when a package changes
- ❌ Does not support independent deployment
- ❌ Less flexible scaling compared to Runtime approach
- ❌ All packages must be compatible at build time
apps/
└── ecommerce-bi-shell/ # Shell application
├── src/
│ └── bootstrap.js # Imports from packages
└── webpack.config.js # Regular webpack configuration
packages/
├── ecommerce-sections-product/ # Package for Product section
│ └── src/
│ └── index.js
├── ecommerce-sections-checkout/ # Package for Checkout section
└── ecommerce-sections-inspire/ # Package for Inspire section
| Characteristic | Monolith | Runtime Integration | Build Integration |
|---|---|---|---|
| Independent deployment | ❌ | ✅ | ❌ |
| Separate development | ❌ | ✅ | ✅ |
| Single bundle | ✅ | ❌ | ✅ |
| Lazy loading modules | ❌ | ✅ | ❌ |
| HTTP requests at runtime | No | Yes (remoteEntry.js) | No |
| Setup complexity | Low | High | Medium |
| Debugging | Simple | Complex | Simple |
| Team scalability | Low | High | Medium |
| Module isolation | None | Full | Partial |
| Dependency versioning | Unified | Problematic | Unified |
- Node.js >= 14.0.0
- npm >= 7.0.0
- Yarn (recommended)
yarn installyarn buildyarn dev:ecommerce-monolithApplication available at: http://localhost:3010/
yarn dev:ecommerce-mf-runtime-integrationThis will start:
- Shell application on port 3100
- Product micro-frontend on port 3101
- Checkout micro-frontend on port 3102
- Inspire micro-frontend on port 3103
Shell application available at: http://localhost:3100/
yarn dev:ecommerce-mf-build-integrationApplication available at: http://localhost:3200/
yarn devShared component library used by all approaches:
@ui-core/web-components- reusable React components (Card, Button, Input, etc.)
Shared configuration packages:
@packages/config- shared configurations (Tailwind, ESLint, Axios)@packages/tsconfig- TypeScript configurations (if used)
The project demonstrates three different approaches to organizing a frontend application in the context of microservices architecture. The choice of approach depends on the specific project requirements:
- Monolith: Suitable for small teams and simple applications
- Runtime Integration: Ideal for large organizations with many independent teams
- Build Integration: A compromise between development flexibility and deployment simplicity