The Problem
Modern enterprise frontends are sprawling. At Spring EQ, we needed to integrate AI capabilities — chat interfaces, generative forms, contextual assistants — across a HELOC platform built on a distributed microfrontend architecture. The challenge wasn't just building the features. It was doing it in a way that didn't couple every team to every AI provider, and that could evolve as the AI landscape shifted.
The Architecture
We settled on React + Module Federation as the foundation. Each microfrontend is independently deployable, owns its own domain logic, and communicates through a shared contract layer rather than direct imports.
// webpack.config.js (host app)
new ModuleFederationPlugin({
name: 'host',
remotes: {
aiChat: 'aiChat@https://cdn.example.com/ai-chat/remoteEntry.js',
loanFlow: 'loanFlow@https://cdn.example.com/loan-flow/remoteEntry.js',
},
shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
})The AI Orchestration Layer
The key insight was building a provider-agnostic AI orchestration layer in the frontend. Rather than calling Claude or OpenAI directly from components, every microfrontend goes through a shared service adapter:
// ai-service/adapter.ts
export interface AIAdapter {
complete(prompt: string, options?: CompletionOptions): Promise<ReadableStream>;
}
export function createAdapter(provider: 'claude' | 'openai' | 'codex'): AIAdapter {
switch (provider) {
case 'claude': return new ClaudeAdapter();
case 'openai': return new OpenAIAdapter();
default: return new CodexAdapter();
}
}This meant we could swap providers without touching a single component — a decision that paid off when we optimized for latency on different use cases.
Lessons Learned
1. Schema validation is non-negotiable. AI responses are unpredictable. We used TypeScript + JSON Schema to validate every response at the boundary, which cut runtime rendering errors by ~30%.
2. Streaming matters for UX. Waiting for a full LLM response before rendering makes the interface feel broken. React Server Components + streaming let us start rendering immediately.
3. Observability from day one. We wired custom hooks to track prompt performance, token usage, and error rates. Without that, tuning was guesswork.
4. Circuit breakers save users. LLMs have latency spikes. We built client-side circuit breakers that fall back to graceful degradation UI states rather than hanging spinners.
Results
- ~30% reduction in runtime rendering errors via schema validation
- ~40% improvement in perceived response time via edge-optimized streaming
- ~35% faster prototyping for new features via AI-assisted development workflows
- ~25% acceleration in feature delivery via the shared AI-aware design system
The microfrontend pattern gave us the isolation we needed to move fast without breaking things. Combined with a disciplined AI orchestration layer, it's become the template for every new AI feature we ship.
