Micro Frontends and Service-Oriented Architecture
Combining Backend For Frontend (BFF) with Micro Frontends
BFF Architecture Pattern
BFF acts as an adaptation layer between the backend and frontend, providing tailored API interfaces for different clients:
Client → BFF → Microservices Cluster
↘ Micro Frontend Application
Implementation Approach:
// BFF layer example (Express)
const express = require('express');
const { getUserProfile } = require('./userService');
const { getOrderList } = require('./orderService');
const app = express();
// API tailored for web clients
app.get('/api/web/profile', async (req, res) => {
const user = await getUserProfile(req.user.id);
const orders = await getOrderList(req.user.id, { limit: 5 });
res.json({
user,
recentOrders: orders,
recommendedProducts: [] // Add business logic as needed
});
});
// API tailored for mobile clients
app.get('/api/mobile/profile', async (req, res) => {
const user = await getUserProfile(req.user.id, { minimal: true });
res.json(user);
});
Advantages:
- Reduces network request frequency
- Hides backend complexity
- Aggregates data on-demand
Role of API Gateway
Core Functions
- Request Routing: Directs requests to the appropriate microservice
- Protocol Conversion: REST ↔ GraphQL ↔ gRPC
- Authentication/Authorization: Centralized identity verification
- Traffic Control: Rate limiting, circuit breaking
- Monitoring and Analytics: Request logging, performance metrics
Implementation Approach:
# Kong API Gateway configuration example
services:
- name: user-service
url: http://user-service:3001
routes:
- paths:
- /api/users
- name: order-service
url: http://order-service:3002
routes:
- paths:
- /api/orders
plugins:
- name: key-auth
route:
- name: user-service
- name: rate-limiting
config:
minute: 100
route:
- name: order-service
Integration with Micro Frontends:
// Micro frontend API proxy layer
const apiUrl = process.env.API_GATEWAY_URL;
export async function fetchUserProfile() {
return fetch(`${apiUrl}/api/web/profile`, {
headers: { 'Authorization': getAuthToken() }
}).then(res => res.json());
}
Service Aggregation and Decoupling
graph TD A[Web Frontend] -->|GraphQL| B[API Gateway] B --> C[User Service] B --> D[Order Service] B --> E[Product Service] C & D & E --> F[Aggregated Response]
Aggregation Pattern
Implementation Approach:
# GraphQL aggregation query example
query GetUserDashboard {
user(id: "123") {
name
avatar
}
recentOrders(limit: 5) {
id
amount
date
}
recommendedProducts {
id
title
price
}
}
Decoupling Strategies
- Event-Driven: Decouple via message queues
// Publish event after order creation eventBus.emit('order.created', orderData); - CQRS Pattern: Separate read and write operations
- Write Service: Handles business logic
- Read Service: Optimizes query performance
Micro Frontends and Serverless
Serverless Architecture in Micro Frontends
Typical Scenarios
- BFF Layer Hosting: Deploy BFF as Serverless functions
- Dynamic Content Rendering: Execute SSR on-demand
- Data Processing Tasks: Scheduled batch jobs
Architecture Diagram:
Client → CDN/Edge → API Gateway → [Serverless BFF] → Microservices
↘ [Serverless SSR]
Function as a Service (FaaS) with Micro Frontends
FaaS Implementation Approach
// AWS Lambda example (Node.js)
exports.handler = async (event) => {
const { path, httpMethod, queryStringParameters } = event;
if (path === '/api/profile' && httpMethod === 'GET') {
const user = await getUserFromDB(queryStringParameters.userId);
return {
statusCode: 200,
body: JSON.stringify(user)
};
}
return {
statusCode: 404,
body: 'Not Found'
};
};
Integration Patterns with Micro Frontends
- Independent Deployment: Each micro frontend has its dedicated FaaS
- Shared FaaS: Common functions deployed centrally
- Hybrid Mode: Dedicated functions for core features, shared for auxiliary ones
Serverless Deployment Practices
Deployment Process
Code Packaging:
# Webpack configuration example
module.exports = {
target: 'node',
externals: ['aws-sdk'], // Exclude AWS SDK from bundle
output: {
libraryTarget: 'commonjs2'
}
};
Deployment Script:
serverless deploy --stage prod --region us-east-1
CI/CD Integration:
# GitHub Actions example
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: npm install
- run: serverless deploy --stage ${{ secrets.STAGE }}
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Performance Optimization
- Cold Start Mitigation:
- Pre-warming strategy (Provisioned Concurrency)
- Lightweight runtime (e.g., Alpine Linux)
- Resource Management:
- Adjust memory/CPU configurations on-demand
- Set reasonable timeout durations
Micro Frontends and Cross-Platform
Micro Frontends with Desktop Applications
Tauri Integration Approach
// Tauri main process configuration
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![fetch_data])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
#[tauri::command]
fn fetch_data() -> Result<String, String> {
// Call micro frontend API
let response = reqwest::get("http://localhost:3000/api/data")
.map_err(|e| e.to_string())?;
response.text().map_err(|e| e.to_string())
}
Comparison with Electron
| Feature | Tauri | Electron |
|---|---|---|
| Bundle Size | ~10MB | ~100MB |
| Performance | High | Medium |
| Security | High | Medium |
| Developer Experience | Rust learning curve | JavaScript-friendly |
Micro Frontends with Mobile
React Native Integration
// React Native micro frontend container
import { MicroFrontend } from 'react-native-microfrontend';
function App() {
return (
<View>
<MicroFrontend
name="user-profile"
url="http://mobile-mf-host/user-profile"
renderLoading={() => <ActivityIndicator />}
renderError={(error) => <Text>Error: {error.message}</Text>}
/>
</View>
);
}
Cross-Platform Communication
// Main application communication with micro frontend
window.ReactNativeWebView.postMessage(JSON.stringify({
type: 'USER_LOGIN',
payload: { userId: 123 }
}));
// Micro frontend receives messages
window.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.type === 'USER_LOGIN') {
// Handle login event
}
});
Cross-Platform Code Reuse
Shared Logic Layer
// shared/business-logic.ts
export function calculateDiscount(price: number, rate: number): number {
return price * (1 - rate);
}
// Web usage
import { calculateDiscount } from 'shared-business-logic';
// Mobile usage (React Native)
import { calculateDiscount } from '@company/shared-business-logic';
UI Component Library
# Create cross-platform component library
npx create-component-library@latest
Component Implementation:
// Button component (supports Web/React Native)
import { Platform } from 'react-native';
export function Button({ children, onPress }) {
if (Platform.OS === 'web') {
return <button onClick={onPress}>{children}</button>;
}
return <TouchableOpacity onPress={onPress}>{children}</TouchableOpacity>;
}
Build Toolchain
// package.json
{
"scripts": {
"build:web": "webpack --config webpack.web.config.js",
"build:rn": "metro-react-native-babel-preset",
"build:all": "npm run build:web && npm run build:rn"
},
"dependencies": {
"@company/shared-ui": "^1.0.0"
}
}
Summary and Best Practices
- Service-Oriented Integration:
- BFF layers should focus on frontend needs, not backend implementation details
- API Gateway configurations should adhere to the principle of least privilege
- Monitor response time SLAs during service aggregation
- Serverless Application:
- Consider reserved instances for cold-start-sensitive scenarios
- Balance function granularity between reusability and independence
- Monitor function execution time and costs
- Cross-Platform Development:
- Prioritize shared logic over UI component reuse
- Use conditional compilation for platform-specific code
- Establish a unified type definition system
- Performance Optimization:
- Implement on-demand loading for micro frontends
- Tune Serverless function memory configurations
- Monitor cross-platform rendering performance
- Security Considerations:
- Enforce API Gateway authentication/authorization
- Securely manage Serverless environment variables
- Encrypt cross-platform data transmission
Through thoughtful architecture design and technology selection, micro frontends can seamlessly integrate into modern enterprise-grade application architectures, achieving true business decoupling and agile delivery. Start with pilot projects to build experience and gradually refine the technical ecosystem.



