Microfrontends con Nx, Parte 2: Cómo Dividir Dominios, Versionar Contratos y Sobrevivir al CI en un Caso Real

Hace un tiempo escribí "Microfrontends en la Era Agéntica: Orquestación, SDD y la Belleza del Monorepo con Nx", donde conté por qué Nx tiene sentido para este tipo de arquitectura. En este segundo artículo comparto la parte práctica: cómo se ven esas decisiones cuando hay que tomarlas con un equipo real y un timeline real.
No repito los fundamentos del primero, pero sí los llevo a un caso aplicado: cómo dividimos dominios en apps y librerías, qué contratos definimos entre microfrontends, cómo los versionamos y qué se complicó en CI cuando el monorepo dejó de ser un proyecto de demo y empezó a comportarse como lo que era: una plataforma con múltiples equipos empujando código al mismo tiempo.
El problema de fondo: dividir por dominio, no por pantalla
La primera tentación cuando armás microfrontends en un monorepo Nx es dividir por ruta o por pantalla. "Esta es la app de login", "esta es la de checkout", "esta es la de perfil". Funciona durante las primeras semanas, y después empieza a doler: cada feature nueva termina tocando cuatro apps distintas porque las pantallas no respetan los límites de dominio, respetan los límites de la navegación.
Lo que nos funcionó fue invertir la pregunta. En lugar de preguntar "¿qué pantallas tenemos?", nos preguntamos "¿qué dominios de negocio tienen ciclo de vida propio, equipo propio y razón de deploy propia?". Es la misma pregunta que uno se hace en un backend con microservicios, y no hay motivo para no hacérsela también del lado del frontend.
Con esa lógica, las apps del monorepo terminaron mapeando a dominios de negocio con dueño claro, y no a secciones de UI. Una app no es "la pantalla de facturación", es "el dominio de facturación, con todo lo que eso implica: sus pantallas, sus estados, sus reglas". Cuando una pantalla necesita mostrar información de dos dominios, eso no se resuelve fusionando apps: se resuelve con composición en runtime o con librerías compartidas bien acotadas, que es para lo que sirve el resto del monorepo.
Apps vs. librerías: la regla que evitó el caos
Una vez que los dominios quedaron claros, la pregunta que nos acompañaba todos los días era: ¿esto va en una app o en una librería? Nx no da esa respuesta gratis, da la estructura para que la respuesta sea consistente.
La regla que adoptamos, y que sostuvimos con disciplina aunque a veces costara:
- Las apps son shells de deploy. Si algo se deployea de forma independiente, es una app. Si no se deployea solo, no es una app, es una librería.
- Las librerías son de tres tipos, y no se mezclan. Librerías de dominio (lógica y componentes específicos de un negocio, consumidas solo por su dominio), librerías compartidas de UI (design system, componentes genéricos, sin lógica de negocio) y librerías de infraestructura (clientes HTTP, utilidades de autenticación, configuración transversal).
- Nada de dominio A importa código de dominio B directamente. Si dos dominios necesitan compartir algo, ese algo sube de nivel: se convierte en librería compartida o en un contrato explícito, nunca en un import cruzado silencioso.
Esta última regla fue la que más reglas de linting nos pidió escribir, pero también la que más nos ayudó. Nx tiene herramientas de boundaries para reforzar justamente esto —restricciones de dependencias entre proyectos basadas en tags—, y las usamos. Una disciplina de arquitectura que se puede violar sin que el CI avise termina siendo un buen deseo más que una regla.
Contratos entre microfrontends: el punto donde todo se pone serio
Acá la conversación deja de ser sobre Nx y pasa a ser sobre arquitectura de sistemas distribuidos, que es, en el fondo, lo que son los microfrontends bien hechos.
Cada microfrontend expone un contrato: qué eventos emite, qué props o parámetros espera para montarse, qué datos necesita del shell o de otros microfrontends para funcionar. La tentación es dejar que ese contrato viva implícito en el código, algo que "todos conocen" porque están en el mismo repo. Es un error que se paga caro apenas el equipo crece o se suma alguien nuevo.
Lo que priorizamos fue tratar esos contratos como se trata un contrato de API: con un esquema explícito, versionado, y con la misma seriedad que se le pondría a un contrato REST o a un evento de un bus de mensajería. En la práctica, esto significó:
- Definir el contrato en un lugar visible del monorepo, no enterrado en la implementación de cada microfrontend. Una librería de tipos o esquemas compartidos que actúa como fuente de verdad de qué espera y qué entrega cada dominio.
- Versionar el contrato, no solo el código. Un cambio en el contrato de un microfrontend puede romper a otro equipo que no está mirando ese código todos los días. Tratamos esos cambios con la misma cautela que un breaking change de API pública: versión nueva, período de convivencia, comunicación explícita.
- Separar el contrato de la implementación. El shell que orquesta los microfrontends solo debería conocer el contrato, nunca los detalles internos de cómo un dominio resuelve su lógica. Eso es lo que permite deployar microfrontends de forma independiente sin que cada deploy sea un evento de riesgo para todo el sistema.
Esta idea de contratos explícitos y versionados conecta directo con el enfoque de Specification-Driven Development que mencioné en el artículo anterior: cuanto más clara y ejecutable es la especificación de lo que un microfrontend promete, menos margen hay para que la integración se rompa en producción por un supuesto que nadie escribió en ningún lado.
Dónde se complicó de verdad: el CI
Si el primer artículo hablaba de la belleza del monorepo, este tiene que hablar también de su fricción, porque omitirla no sería honesto. Y la fricción más real apareció en CI, no en el código.
Cuando varios equipos empujan a distintas apps y librerías del mismo monorepo, un pipeline de CI que corre "todo, siempre" se vuelve lento e insostenible rápidamente. Y un pipeline lento no es solo un problema de productividad, es un problema de arquitectura: si el feedback tarda demasiado, la gente empieza a saltarse validaciones, y ahí es donde los contratos entre microfrontends empiezan a romperse sin que nadie lo note hasta producción.
Lo que se complicó específicamente:
- Correr todo el pipeline por cada commit dejó de ser viable apenas el número de apps y librerías creció. Correr los tests y builds de dominios que ni siquiera fueron tocados por un cambio es desperdiciar tiempo de CI.
- Detectar qué se rompía realmente cuando cambiaba una librería compartida se volvía cada vez más manual, justo cuando más automático necesitaba ser.
- Coordinar deploys independientes con contratos versionados exigía que el CI no solo supiera qué construir, sino qué combinaciones de versiones eran compatibles entre microfrontends.
La resolución pasó por apoyarnos en algo que Nx ya resuelve muy bien: el grafo de dependencias del monorepo. En lugar de correr todo siempre, el pipeline pasó a ejecutar solo lo que el grafo identifica como afectado por un cambio dado. Eso, sumado al cacheo de tareas ya ejecutadas, redujo bastante el tiempo de feedback sin sacrificar cobertura: si un cambio no toca un dominio, ese dominio no se vuelve a construir ni testear en ese pipeline.
La segunda pieza fue la disciplina alrededor de los boundaries que mencioné antes: si el CI valida automáticamente que nadie violó las reglas de dependencia entre dominios, gran parte de los problemas de integración se detectan antes de llegar a un pull request revisado por humanos, no después.
Qué me llevo de este caso
- Los dominios de negocio son mejores límites que las pantallas. Preguntarse quién es dueño de qué y por qué se deployea por separado ordena más que dividir por navegación.
- La regla app vs. librería, sostenida con linting, vale lo que cuesta. Los boundaries automáticos de Nx convierten una convención en algo verificable.
- Los contratos entre microfrontends se tratan como contratos de API: explícitos, versionados y con período de convivencia.
- El CI también es arquitectura. Un pipeline basado en el grafo de afectados y en cache mantiene el feedback rápido, y eso protege los contratos.
- Nada de esto fue gratis. Dividir por dominio implica conversaciones incómodas con el negocio sobre dónde están los límites, versionar contratos implica más ceremonia que "simplemente importar el componente", y optimizar el CI es tiempo de ingeniería en algo que nadie fuera del equipo ve. Aun así, un monorepo sin límites claros ni contratos explícitos no es más simple: es más simple solo al principio.
Si el primer artículo era sobre por qué Nx y los microfrontends bien diseñados tienen sentido en la era agéntica, este es sobre cómo se ve eso al sostenerlo en el día a día con un equipo. Si estás trabajando en algo parecido, me interesa intercambiar experiencias: podés escribirme por LinkedIn. Y si te sirve, en mi portfolio cuento cómo trabajo este tipo de proyectos: estebanburgos.com.ar/portfolio.

Sobre el autor
ESTEBAN BURGOS · Tech Lead Frontend · Ingeniero de soluciones de punta a punta
Construyo software de punta a punta para pymes y empresas: webs, plataformas y soluciones con IA. Escribo sobre lo que aprendo en proyectos reales.
Conocé mi trayectoria¿Querés algo así en tu empresa?
Contale tu idea a Tuki y en minutos tenés un rango de precio en tu moneda.
Seguí leyendo
- Lecciones de construir una plataforma de trading para una empresa de energía: seguridad, tiempo real y backofficeComo Forward Deployed Engineer participé, junto a un equipo, en una plataforma de inversiones y trading para una empresa del sector energía con su backoffice de operaciones. Comparto aprendizajes de arquitectura y de proceso, sin código ni datos internos.
- Cuánto gasta un equipo en agentes de IA y cómo medirloDespués de medir tokens por tarea, el siguiente paso es entender cuánto cuesta cada tarea que resuelven los agentes de IA. Comparto cómo elegir el modelo según la tarea, qué registrar al cerrarla y qué mirar en un tablero de costos.
- SDD en la práctica: la plantilla de spec que uso con agentes de IALa plantilla de spec que uso a diario con agentes de IA, con el porqué de cada sección: resumen, criterios de aceptación y los gates que evitan que el código avance sin especificación. También cómo elijo entre flow lite y full y por qué registro el consumo de tokens.
Comentarios
Sé el primero en comentar.