Nota técnica
Microfrontends + BFF en banca regulada
Cómo desacoplar 3 equipos frontend en un proyecto MEP de 4 meses, con BFF en Node.js orquestando 12+ servicios Oracle — sin fragmentar la UX.
En números
4 meses
A go-live
3
Microfrontends
12+
Servicios Oracle
100%
TypeScript
El contexto
En proyectos bancarios de alta criticidad, varios equipos frontend necesitan avanzar en paralelo sobre un backend legacy (Oracle, servicios SOAP/REST heterogéneos). El desafío no es solo técnico: hay compliance, plazos fijos y cero tolerancia a regresiones en flujos de dinero. En Santander MEP hipotecario teníamos 4 meses y 3 squads con dominios distintos: onboarding, simulación y confirmación.
Por qué microfrontends (y cuándo no)
La arquitectura microfrontend permite que cada squad posea su dominio de UI con deploy independiente. El costo es coordinación: design system compartido, contratos entre shells y versionado de dependencias comunes. La regla que aplicamos: microfrontend solo donde el acoplamiento de equipos lo justificaba — no fragmentar por capas técnicas ("MFE de botones"). Tres MFEs, tres equipos, tres flujos de negocio.
Contratos tipados BFF ↔ React
El error más caro fue intentar compartir tipos ad-hoc. Definimos contratos en TypeScript compartidos entre BFF y cada MFE. El BFF expone endpoints con shape estable; los MFEs nunca conocen Oracle directamente.
// packages/shared-types/src/mep-simulation.ts
export interface SimulationRequest {
mortgageId: string;
amount: number;
currency: "USD" | "ARS";
}
export interface SimulationResponse {
rate: number;
totalCost: number;
breakdown: { label: string; value: number }[];
expiresAt: string; // ISO — el BFF normaliza timezone
}
// El MFE solo consume SimulationResponse.
// El BFF traduce SOAP/Oracle → este shape.El rol del BFF
La capa Backend-for-Frontend en Node.js actúa como traductor: agrega llamadas a múltiples servicios Oracle, normaliza errores, adapta payloads al shape que React necesita y oculta la complejidad del backend al cliente. Un solo punto de entrada tipado reduce bugs de integración ~40% vs. adapters por equipo.
// bff/src/routes/simulation.ts
export async function getSimulation(req: Request, res: Response) {
try {
const [rate, limits, customer] = await Promise.all([
oracleClient.getRate(req.body.mortgageId),
oracleClient.getLimits(req.body.mortgageId),
oracleClient.getCustomer(req.user.id),
]);
const response: SimulationResponse = mapOracleToSimulation({
rate, limits, customer, amount: req.body.amount,
});
return res.json(response);
} catch (error) {
// Error normalizado — el MFE nunca ve stack Oracle
return res.status(mapOracleError(error)).json({
code: "SIMULATION_FAILED",
message: getUserFacingMessage(error),
});
}
}Shell host mínimo
El shell host no es un monolito disfrazado: solo routing entre MFEs, autenticación compartida y layout base. Cada MFE se monta lazy. Si el shell crece demasiado, volvés al acoplamiento que querías evitar.
// shell/src/App.tsx
const OnboardingMFE = lazy(() => import("mfe_onboarding/App"));
const SimulationMFE = lazy(() => import("mfe_simulation/App"));
const ConfirmationMFE = lazy(() => import("mfe_confirmation/App"));
export function App() {
return (
<AuthProvider>
<ShellLayout>
<Routes>
<Route path="/onboarding/*" element={<OnboardingMFE />} />
<Route path="/simulation/*" element={<SimulationMFE />} />
<Route path="/confirm/*" element={<ConfirmationMFE />} />
</Routes>
</ShellLayout>
</AuthProvider>
);
}Lecciones aplicadas
Entregamos en 4 meses priorizando: (1) contratos tipados antes de escribir UI, (2) design system mínimo acordado en semana 2, (3) code review cruzado entre squads, (4) entrega incremental por flujo de negocio. La clave fue no sobre-ingenierizar: BFF delgado, MFEs acotados, Oracle invisible para React.
Takeaways
- BFF como orquestador delgado — no un segundo backend monolítico
- Contratos TypeScript compartidos antes de paralelizar equipos
- 3 MFEs = 3 flujos de negocio, no 3 capas técnicas
- Errores Oracle normalizados en BFF — UX consistente en todos los MFEs
- Go-live en 4 meses con 3 squads en paralelo sin acoplar releases