Lesson 13-Advanced Micro Frontend Architecture Design

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

  1. Request Routing: Directs requests to the appropriate microservice
  2. Protocol Conversion: REST ↔ GraphQL ↔ gRPC
  3. Authentication/Authorization: Centralized identity verification
  4. Traffic Control: Rate limiting, circuit breaking
  5. 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

  1. Event-Driven: Decouple via message queues // Publish event after order creation eventBus.emit('order.created', orderData);
  2. 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

  1. BFF Layer Hosting: Deploy BFF as Serverless functions
  2. Dynamic Content Rendering: Execute SSR on-demand
  3. 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

  1. Independent Deployment: Each micro frontend has its dedicated FaaS
  2. Shared FaaS: Common functions deployed centrally
  3. 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

  1. Cold Start Mitigation:
    • Pre-warming strategy (Provisioned Concurrency)
    • Lightweight runtime (e.g., Alpine Linux)
  2. 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

FeatureTauriElectron
Bundle Size~10MB~100MB
PerformanceHighMedium
SecurityHighMedium
Developer ExperienceRust learning curveJavaScript-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

  1. 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
  2. Serverless Application:
    • Consider reserved instances for cold-start-sensitive scenarios
    • Balance function granularity between reusability and independence
    • Monitor function execution time and costs
  3. Cross-Platform Development:
    • Prioritize shared logic over UI component reuse
    • Use conditional compilation for platform-specific code
    • Establish a unified type definition system
  4. Performance Optimization:
    • Implement on-demand loading for micro frontends
    • Tune Serverless function memory configurations
    • Monitor cross-platform rendering performance
  5. 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.

Share your love