Skip to main content

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 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>
);
});

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:

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>
);
});

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