Scaling Applications
Learn how to scale your Refract applications to handle millions of users, complex state management, and enterprise-level requirements.
Architecture Patterns for Scale
Design your application architecture to handle growth:
🏗️
Micro-Frontend Architecture
Break large applications into smaller, independently deployable pieces that teams can work on separately.
🔄
State Management at Scale
Implement patterns for managing complex state across large applications with multiple teams.
⚡
Performance Optimization
Optimize bundle size, loading times, and runtime performance for large-scale applications.
Micro-Frontend Implementation
Split your application into manageable micro-frontends:
- Shell Application
- Micro-Frontend
Shell App - Main Container
import { createComponent } from 'refract';
import { loadMicroFrontend } from './utils/microFrontendLoader';
const ShellApp = createComponent(({ lens }) => {
const currentRoute = lens.useRefraction(window.location.pathname);
const loadedApps = lens.useRefraction(new Map());
// Global state that micro-frontends can access
const globalState = lens.useRefraction({
user: null,
theme: 'light',
permissions: []
});
lens.useEffect(() => {
const handleRouteChange = () => {
currentRoute.set(window.location.pathname);
};
window.addEventListener('popstate', handleRouteChange);
return () => window.removeEventListener('popstate', handleRouteChange);
}, []);
const loadApp = async (appName, mountPoint) => {
if (loadedApps.value.has(appName)) return;
try {
const app = await loadMicroFrontend(appName);
app.mount(mountPoint, {
globalState: globalState.value,
onStateChange: (newState) => globalState.set(newState)
});
loadedApps.set(new Map(loadedApps.value.set(appName, app)));
} catch (error) {
console.error(`Failed to load micro-frontend: ${appName}`, error);
}
};
return (
<div className="shell-app">
<header className="global-header">
<Navigation user={globalState.value.user} />
</header>
<main className="content-area">
{currentRoute.value.startsWith('/dashboard') && (
<MicroFrontendContainer
appName="dashboard"
onLoad={(mountPoint) => loadApp('dashboard', mountPoint)}
/>
)}
{currentRoute.value.startsWith('/analytics') && (
<MicroFrontendContainer
appName="analytics"
onLoad={(mountPoint) => loadApp('analytics', mountPoint)}
/>
)}
{currentRoute.value.startsWith('/settings') && (
<MicroFrontendContainer
appName="settings"
onLoad={(mountPoint) => loadApp('settings', mountPoint)}
/>
)}
</main>
</div>
);
});
const MicroFrontendContainer = createComponent(({ lens, appName, onLoad }) => {
const containerRef = lens.useRefraction(null);
const isLoading = lens.useRefraction(true);
lens.useEffect(() => {
if (containerRef.value) {
onLoad(containerRef.value).finally(() => {
isLoading.set(false);
});
}
}, [containerRef.value]);
return (
<div className="micro-frontend-container">
{isLoading.value && <div className="loading">Loading {appName}...</div>}
<div ref={(el) => containerRef.set(el)} className="micro-frontend-mount" />
</div>
);
});
Dashboard Micro-Frontend
import { createApp, createComponent } from 'refract';
// Dashboard micro-frontend
const DashboardApp = createComponent(({ lens, globalState, onStateChange }) => {
const dashboardData = lens.useRefraction(null);
const loading = lens.useRefraction(true);
// Access global state
const user = globalState?.user;
const theme = globalState?.theme || 'light';
lens.useEffect(() => {
if (user) {
fetchDashboardData(user.id)
.then(data => {
dashboardData.set(data);
loading.set(false);
})
.catch(error => {
console.error('Failed to load dashboard data:', error);
loading.set(false);
});
}
}, [user?.id]);
const updateGlobalTheme = (newTheme) => {
onStateChange?.({
...globalState,
theme: newTheme
});
};
if (loading.value) {
return <div className="dashboard-loading">Loading dashboard...</div>;
}
return (
<div className={`dashboard theme-${theme}`}>
<div className="dashboard-header">
<h1>Welcome, {user?.name}</h1>
<button onClick={() => updateGlobalTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
</div>
<div className="dashboard-content">
<MetricsPanel data={dashboardData.value?.metrics} />
<RecentActivity data={dashboardData.value?.activity} />
<QuickActions user={user} />
</div>
</div>
);
});
// Export micro-frontend interface
export const mount = (element, props = {}) => {
const app = createApp(DashboardApp, {
props
});
app.mount(element);
return {
unmount: () => app.unmount(),
update: (newProps) => app.updateProps(newProps)
};
};
// Webpack Module Federation configuration
export default {
name: 'dashboard',
exposes: {
'./App': './src/App.jsx'
},
shared: {
'refract': { singleton: true },
'react': { singleton: true },
'react-dom': { singleton: true }
}
};
Global State Management at Scale
Implement scalable state management patterns:
Enterprise State Management
import { createRefraction } from 'refract';
// Global state store with modules
class GlobalStateStore {
constructor() {
this.modules = new Map();
this.subscribers = new Set();
}
registerModule(name, initialState, actions) {
const moduleState = createRefraction(initialState);
const moduleActions = {};
Object.keys(actions).forEach(actionName => {
moduleActions[actionName] = (...args) => {
const result = actions[actionName](moduleState.value, ...args);
if (result !== undefined) {
moduleState.set(result);
this.notifySubscribers(name, moduleState.value);
}
};
});
this.modules.set(name, {
state: moduleState,
actions: moduleActions
});
return moduleActions;
}
getModule(name) {
return this.modules.get(name);
}
subscribe(callback) {
this.subscribers.add(callback);
return () => this.subscribers.delete(callback);
}
notifySubscribers(moduleName, newState) {
this.subscribers.forEach(callback => {
callback(moduleName, newState);
});
}
}
// Global store instance
const globalStore = new GlobalStateStore();
// User module
const userActions = globalStore.registerModule('user', {
currentUser: null,
permissions: [],
preferences: {}
}, {
setUser: (state, user) => ({
...state,
currentUser: user,
permissions: user.permissions || []
}),
updatePreferences: (state, preferences) => ({
...state,
preferences: { ...state.preferences, ...preferences }
}),
logout: (state) => ({
currentUser: null,
permissions: [],
preferences: {}
})
});
// Application module
const appActions = globalStore.registerModule('app', {
theme: 'light',
language: 'en',
notifications: [],
loading: false
}, {
setTheme: (state, theme) => ({ ...state, theme }),
setLanguage: (state, language) => ({ ...state, language }),
addNotification: (state, notification) => ({
...state,
notifications: [...state.notifications, { ...notification, id: Date.now() }]
}),
removeNotification: (state, id) => ({
...state,
notifications: state.notifications.filter(n => n.id !== id)
}),
setLoading: (state, loading) => ({ ...state, loading })
});
// Hook for using global state in components
const useGlobalState = (lens, moduleName) => {
const module = globalStore.getModule(moduleName);
if (!module) throw new Error(`Module ${moduleName} not found`);
const localState = lens.useRefraction(module.state.value);
lens.useEffect(() => {
const unsubscribe = globalStore.subscribe((changedModule, newState) => {
if (changedModule === moduleName) {
localState.set(newState);
}
});
return unsubscribe;
}, []);
return {
state: localState.value,
actions: module.actions
};
};
// Usage in components
const UserProfile = createComponent(({ lens }) => {
const { state: userState, actions: userActions } = useGlobalState(lens, 'user');
const { state: appState, actions: appActions } = useGlobalState(lens, 'app');
const handleThemeChange = (newTheme) => {
appActions.setTheme(newTheme);
userActions.updatePreferences({ theme: newTheme });
};
return (
<div className={`user-profile theme-${appState.theme}`}>
<h2>{userState.currentUser?.name}</h2>
<button onClick={() => handleThemeChange(appState.theme === 'light' ? 'dark' : 'light')}>
Switch to {appState.theme === 'light' ? 'Dark' : 'Light'} Theme
</button>
</div>
);
});
Performance Optimization at Scale
Optimize for large applications:
- Code Splitting
- Virtualization
Advanced Code Splitting
import { lazy, Suspense } from 'react';
import { createComponent } from 'refract';
// Route-based code splitting
const routes = {
'/dashboard': lazy(() => import('./pages/Dashboard')),
'/analytics': lazy(() => import('./pages/Analytics')),
'/settings': lazy(() => import('./pages/Settings')),
'/reports': lazy(() => import('./pages/Reports'))
};
// Component-based code splitting
const LazyComponents = {
DataTable: lazy(() => import('./components/DataTable')),
Chart: lazy(() => import('./components/Chart')),
FileUploader: lazy(() => import('./components/FileUploader'))
};
const Router = createComponent(({ lens }) => {
const currentRoute = lens.useRefraction('/dashboard');
const loadedComponents = lens.useRefraction(new Set());
const preloadRoute = (route) => {
if (routes[route] && !loadedComponents.value.has(route)) {
// Preload on hover/focus
routes[route]().then(() => {
loadedComponents.set(new Set([...loadedComponents.value, route]));
});
}
};
const RouteComponent = routes[currentRoute.value];
return (
<div className="app-router">
<nav>
{Object.keys(routes).map(route => (
<button
key={route}
onClick={() => currentRoute.set(route)}
onMouseEnter={() => preloadRoute(route)}
className={currentRoute.value === route ? 'active' : ''}
>
{route.replace('/', '')}
</button>
))}
</nav>
<main>
<Suspense fallback={<div className="route-loading">Loading...</div>}>
{RouteComponent && <RouteComponent />}
</Suspense>
</main>
</div>
);
});
// Smart component loading
const ConditionalComponent = createComponent(({ lens, componentName, condition, ...props }) => {
const Component = LazyComponents[componentName];
const shouldLoad = lens.useRefraction(false);
lens.useEffect(() => {
if (condition) {
shouldLoad.set(true);
}
}, [condition]);
if (!shouldLoad.value) {
return <div className="component-placeholder">Component will load when needed</div>;
}
return (
<Suspense fallback={<div className="component-loading">Loading {componentName}...</div>}>
<Component {...props} />
</Suspense>
);
});
Virtual Scrolling for Large Lists
const VirtualizedList = createComponent(({ lens, items, itemHeight = 50, containerHeight = 400 }) => {
const scrollTop = lens.useRefraction(0);
const containerRef = lens.useRefraction(null);
const visibleRange = lens.useRefraction(() => {
const start = Math.floor(scrollTop.value / itemHeight);
const visibleCount = Math.ceil(containerHeight / itemHeight);
const end = Math.min(start + visibleCount + 1, items.length);
return { start, end };
});
const handleScroll = (e) => {
scrollTop.set(e.target.scrollTop);
};
const totalHeight = items.length * itemHeight;
const offsetY = visibleRange.value.start * itemHeight;
const visibleItems = items.slice(visibleRange.value.start, visibleRange.value.end);
return (
<div
ref={(el) => containerRef.set(el)}
className="virtualized-list"
style={{ height: containerHeight, overflow: 'auto' }}
onScroll={handleScroll}
>
<div style={{ height: totalHeight, position: 'relative' }}>
<div style={{ transform: `translateY(${offsetY}px)` }}>
{visibleItems.map((item, index) => (
<div
key={visibleRange.value.start + index}
style={{ height: itemHeight }}
className="list-item"
>
<ListItem item={item} />
</div>
))}
</div>
</div>
</div>
);
});
// Virtualized table for large datasets
const VirtualizedTable = createComponent(({ lens, data, columns, rowHeight = 40 }) => {
const scrollState = lens.useRefraction({ top: 0, left: 0 });
const containerSize = lens.useRefraction({ width: 0, height: 0 });
const visibleRows = lens.useRefraction(() => {
const start = Math.floor(scrollState.value.top / rowHeight);
const count = Math.ceil(containerSize.value.height / rowHeight) + 1;
const end = Math.min(start + count, data.length);
return { start, end, items: data.slice(start, end) };
});
return (
<div className="virtualized-table">
<div className="table-header">
{columns.map(column => (
<div key={column.key} className="header-cell" style={{ width: column.width }}>
{column.title}
</div>
))}
</div>
<div
className="table-body"
style={{ height: 400, overflow: 'auto' }}
onScroll={(e) => scrollState.set({
top: e.target.scrollTop,
left: e.target.scrollLeft
})}
>
<div style={{ height: data.length * rowHeight, position: 'relative' }}>
<div style={{ transform: `translateY(${visibleRows.value.start * rowHeight}px)` }}>
{visibleRows.value.items.map((row, index) => (
<div key={visibleRows.value.start + index} className="table-row" style={{ height: rowHeight }}>
{columns.map(column => (
<div key={column.key} className="table-cell" style={{ width: column.width }}>
{column.render ? column.render(row[column.key], row) : row[column.key]}
</div>
))}
</div>
))}
</div>
</div>
</div>
</div>
);
});
Caching Strategies
Implement effective caching for better performance:
Multi-Level Caching System
class CacheManager {
constructor() {
this.memoryCache = new Map();
this.sessionCache = sessionStorage;
this.persistentCache = localStorage;
this.maxMemorySize = 100; // Max items in memory
}
set(key, value, options = {}) {
const { ttl = 300000, persistent = false, level = 'memory' } = options;
const item = {
value,
timestamp: Date.now(),
ttl,
persistent
};
// Memory cache
if (level === 'memory' || level === 'all') {
if (this.memoryCache.size >= this.maxMemorySize) {
const firstKey = this.memoryCache.keys().next().value;
this.memoryCache.delete(firstKey);
}
this.memoryCache.set(key, item);
}
// Session cache
if (level === 'session' || level === 'all') {
try {
this.sessionCache.setItem(key, JSON.stringify(item));
} catch (e) {
console.warn('Session storage full, clearing old items');
this.clearExpired('session');
}
}
// Persistent cache
if (persistent || level === 'persistent' || level === 'all') {
try {
this.persistentCache.setItem(key, JSON.stringify(item));
} catch (e) {
console.warn('Local storage full, clearing old items');
this.clearExpired('persistent');
}
}
}
get(key) {
// Check memory first
let item = this.memoryCache.get(key);
// Check session storage
if (!item) {
try {
const stored = this.sessionCache.getItem(key);
if (stored) {
item = JSON.parse(stored);
// Promote to memory cache
this.memoryCache.set(key, item);
}
} catch (e) {
console.warn('Failed to parse session cache item:', key);
}
}
// Check persistent storage
if (!item) {
try {
const stored = this.persistentCache.getItem(key);
if (stored) {
item = JSON.parse(stored);
// Promote to memory cache
this.memoryCache.set(key, item);
}
} catch (e) {
console.warn('Failed to parse persistent cache item:', key);
}
}
// Check if expired
if (item && Date.now() - item.timestamp > item.ttl) {
this.delete(key);
return null;
}
return item?.value || null;
}
delete(key) {
this.memoryCache.delete(key);
this.sessionCache.removeItem(key);
this.persistentCache.removeItem(key);
}
clearExpired(level = 'all') {
const now = Date.now();
if (level === 'memory' || level === 'all') {
for (const [key, item] of this.memoryCache.entries()) {
if (now - item.timestamp > item.ttl) {
this.memoryCache.delete(key);
}
}
}
if (level === 'session' || level === 'all') {
this.clearStorageExpired(this.sessionCache);
}
if (level === 'persistent' || level === 'all') {
this.clearStorageExpired(this.persistentCache);
}
}
clearStorageExpired(storage) {
const keysToDelete = [];
const now = Date.now();
for (let i = 0; i < storage.length; i++) {
const key = storage.key(i);
try {
const item = JSON.parse(storage.getItem(key));
if (item && now - item.timestamp > item.ttl) {
keysToDelete.push(key);
}
} catch (e) {
keysToDelete.push(key); // Remove invalid items
}
}
keysToDelete.forEach(key => storage.removeItem(key));
}
}
const cacheManager = new CacheManager();
// Cached API hook
const useCachedApi = (lens, url, options = {}) => {
const data = lens.useRefraction(null);
const loading = lens.useRefraction(false);
const error = lens.useRefraction(null);
const cacheKey = `api:${url}:${JSON.stringify(options)}`;
lens.useEffect(() => {
// Check cache first
const cached = cacheManager.get(cacheKey);
if (cached) {
data.set(cached);
return;
}
// Fetch from API
loading.set(true);
error.set(null);
fetch(url, options)
.then(response => response.json())
.then(result => {
data.set(result);
// Cache the result
cacheManager.set(cacheKey, result, {
ttl: 300000, // 5 minutes
level: 'session'
});
})
.catch(err => {
error.set(err.message);
})
.finally(() => {
loading.set(false);
});
}, [url, JSON.stringify(options)]);
return {
data: data.value,
loading: loading.value,
error: error.value,
invalidate: () => cacheManager.delete(cacheKey)
};
};
Infrastructure Scaling
Scale your infrastructure to handle growth:
Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: refract-app
spec:
replicas: 10
selector:
matchLabels:
app: refract-app
template:
metadata:
labels:
app: refract-app
spec:
containers:
- name: refract-app
image: your-registry/refract-app:latest
ports:
- containerPort: 80
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
env:
- name: NODE_ENV
value: "production"
livenessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 80
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: refract-app-service
spec:
selector:
app: refract-app
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: refract-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: refract-app
minReplicas: 3
maxReplicas: 50
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
Best Practices for Scale
- Modular architecture: Break applications into manageable pieces
- Lazy loading: Load code and data only when needed
- Caching strategy: Implement multi-level caching for better performance
- State management: Use centralized state management for complex applications
- Performance monitoring: Continuously monitor and optimize performance
- Infrastructure as code: Automate infrastructure deployment and scaling
- Team organization: Structure teams around business domains, not technical layers
Related Topics
- Performance Monitoring - Monitor application performance
- CI/CD Pipeline - Automated deployment at scale