Read in English: English ver.
Este repositório documenta um case study real de arquitetura frontend, construído como uma prova de conceito arquitetural, com potencial de aplicação em cenários institucionais de larga escala.
Atuei como Frontend Technical Lead, sendo responsável por definir a arquitetura frontend, propor e validar o uso de Micro Frontends com Module Federation e selecionar o Angular 17 como framework base, priorizando estabilidade e adequação a ambientes corporativos.
Apesar de ter sido inicialmente desenvolvido por um time pequeno e sob restrições de ferramentas e financiamento, a arquitetura foi intencionalmente projetada para suportar escalabilidade de longo prazo, evolução independente de domínios e crescimento organizacional.
Leitura da PoC & Referências 🏹
Sistemas legados de gestão acadêmica utilizados por universidades públicas brasileiras são, em geral, monolíticos, fortemente acoplados e pouco adequados a requisitos modernos de usabilidade e escalabilidade.
Este projeto surgiu como uma prova de conceito para uma nova plataforma com o objetivo de:
- atender múltiplas universidades públicas
- permitir autonomia institucional
- escalar entre times e domínios
- evoluir de forma independente ao longo do tempo
O principal desafio foi projetar uma arquitetura frontend capaz de sustentar manutenibilidade de longo prazo e escalabilidade organizacional em um contexto de incerteza institucional e recursos limitados.
- Implantação inicial limitada a uma única universidade para fins de validação da prova de conceito.
- Expansão futura condicionada à aprovação e financiamento governamental (MEC)
- Ausência de uma estratégia de migração previamente definida a partir de sistemas legados
- Frontend com necessidade de evolução independente entre domínios
- Arquitetura flexível quanto a estratégias de adoção e implantação
- Ausência de financiamento governamental inicial
- Time de design operando com ferramentas gratuitas
- O design visual da interface e diretrizes de UI foram concebidos por Geysla Bezerra (UX UI Designer), servindo como base para a implementação dos componentes frontend.
- Sem acesso a bibliotecas pagas de componentes ou UI kits
Como consequência, todos os componentes de interface foram desenhados e implementados manualmente.
As decisões arquiteturais foram principalmente orientadas pelos seguintes requisitos não funcionais:
- Deploys independentes, reduzindo overhead de coordenação
- Clareza de ownership de domínios, suportando crescimento futuro de times
- Blast radius controlado, em mudanças de frontend
- Manutenibilidade de longo prazo acima de velocidade inicial de entrega
- Flexibilidade diante de incertezas institucionais
- Setup inicial mais simples
- Alto acoplamento e deploy centralizado
→ Rejeitada devido a riscos de escalabilidade
- Melhor organização interna
- Ainda um único artefato de deploy
→ Insuficiente para escalar organizacionalmente
- Deploys independentes
- Clareza de domínios
- Evolução desacoplada entre áreas funcionais → Selecionada para validação, apesar da maior complexidade
- Micro Frontends foram propostos e defendidos como uma escolha estratégica para suportar escalabilidade organizacional de longo prazo.
- Module Federation (Webpack 5) foi selecionado para composição em tempo de execução e compartilhamento controlado de dependências.
- Angular 17 foi escolhido por maturidade do framework, estabilidade e adequação a aplicações de grande porte.
- Uma Shared UI Library foi introduzida desde o início para centralizar componentes reutilizáveis e garantir consistência visual sob restrições de tooling, projetada para evoluir de forma independente dos MFEs, evitando duplicação de componentes e divergência visual.
Todas as decisões arquiteturais de frontend foram lideradas e validadas pela Frontend Technical Lead.
As principais decisões arquiteturais desta PoC foram registradas formalmente como ADRs (Architecture Decision Records), garantindo rastreabilidade técnica e clareza de critérios de escolha. O uso de ADRs foi adotado para evitar decisões implícitas, documentar trade-offs e permitir evolução consciente da arquitetura ao longo do tempo.
Autora: Gabrielly Trajano Amorin, 2026, Microfrontends PoC — SigUni
Versão PDF: Desenho da arquitetura em PDF
-
Shell Application
- Ponto de entrada da aplicação
- Orquestração de rotas
- Carregamento dinâmico dos MFEs
- Integração via Route-based Module Federation
-
Micro Frontends
- Isolados por domínio funcional
- Deploy independente
- Ausência de acoplamento direto entre MFEs
-
Shared UI Library
- Componentes reutilizáveis
- Padrões visuais consistentes
- Implementação manual com HTML semântico e estilos utilitários
A comunicação entre MFEs foi intencionalmente limitada a limites de navegação.
A arquitetura de integração Shell ↔ Micro Frontends foi validada com testes End-to-End automatizados.
Os testes cobrem:
-
carregamento de MFEs via Module Federation
-
resolução de rotas remotas no Shell
-
composição correta de prefixos de rota (shell + rotas internas do MFE)
-
renderização de fallback quando um microfrontend remoto falha
Esses testes foram usados como mecanismo de verificação de boundary, roteamento e estratégia de fallback da arquitetura proposta. Os testes têm como objetivo validar contratos e integração, não substituir uma suíte completa de testes de produto.
Ver decisão detalhada de integração Shell ↔ Micro Frontends
A integração entre Shell e Micro Frontends adota Route-based Module Federation, onde cada MFE expõe rotas Angular e é carregado dinamicamente via lazy loading do router.
Lifecycle imperativo:
mount(container)
unmount()
Rejeitado por aumentar complexidade runtime e romper integração nativa com Angular Router.
- Todos os MFEs usam Angular
- Boundary natural baseado em rota
- Compatível com guards, resolvers e DI
- Sem lifecycle manual de DOM
- Melhor testabilidade
- Menor código de infraestrutura
remoteEntry → exposed routes → router lazy load → módulo → render
A integração entre Shell e Micro Frontends foi implementada com um contrato explícito e defensivo, visando reduzir acoplamento e falhas em runtime.
O Shell define um contrato mínimo esperado de cada MFE:
remoteEntry: endereço do remoteexposedModule: módulo exposto via Module FederationroutePath: prefixo de rota definido no Shell
interface RemoteRouteContract {
remoteEntry: string;
exposedModule: string;
routePath: string;
}Esse contrato é centralizado no Shell e tipado como:
Record<MfeKey, RemoteRouteContract>garantindo segurança em tempo de build contra chaves inválidas.
Essa estratégia foi adotada considerando o contexto de deploys independentes entre Shell e Micro Frontends, onde contratos podem falhar em tempo de execução. O objetivo foi tratar falhas de integração como eventos esperados, garantindo previsibilidade e isolamento de impacto.
Como contratos podem falhar em tempo de execução (deploy independente), o loader recebe o contrato como unknown e realiza validação explícita antes do carregamento:
assertRemoteContract(config);Essa abordagem evita confiar apenas na tipagem estática e garante falhas previsíveis.
O loader de MFEs é tratado como infraestrutura pura e retorna um estado explícito:
-
ready: rotas carregadas com sucesso
-
failed: erro durante o carregamento
O loader não decide fallback nem contém lógica de UX.
A decisão de fallback é responsabilidade do Router do Shell, permitindo:
-
controle explícito de erro
-
observabilidade
-
possibilidade futura de retry
Esse desenho mantém separação clara entre infraestrutura e experiência do usuário.
Cada Micro Frontend expõe explicitamente um símbolo ROUTES via Module Federation:
export const ROUTES: Routes = [...]O Shell carrega apenas rotas, não componentes, reforçando o padrão de route-based federation e evitando lifecycle imperativo.
Ver decisão de agrupamento de Papéis, Permissões e Recursos
Papéis, Permissões e Recursos foram agrupados dentro de um único microfrontend de Access Control.
- Alta coesão funcional
- Mudança frequente conjunta
- Mesmo fluxo de autorização
- Redução de contratos inter-MFE
- Menor complexidade de integração
Separar em MFEs distintos geraria:
- fragmentação de domínio
- maior coordenação
- mais contratos runtime
- mais pontos de falha
subdomínio coeso + taxa de mudança conjunta + fluxo funcional integrado
Tradeoff aceito: deploy conjunto dentro do subdomínio de segurança.
Ver regras de definição de rotas em arquitetura com Module Federation
O Shell é responsável por definir os prefixos de rota de domínio para cada Micro Frontend.
Cada MFE é montado sob um prefixo único definido no router do Shell.
Exemplo:
// shell/app.routes.ts
{
path: 'users',
loadChildren: () => loadRemoteRoutes(...)
}Resultado:
/users → ativa o MFE de usuários
O prefixo de domínio é sempre definido no Shell, nunca no remote.
Cada Micro Frontend define apenas rotas internas relativas, sem repetir o prefixo de domínio.
Exemplo correto no remote:
export const routes = [
{ path: '', component: UserListPage },
{ path: 'new', component: UserCreatePage },
{ path: ':id', component: UserDetailPage }
];URLs finais:
/users
/users/new
/users/123
URL final = shell prefix + remote child route
Não declarar prefixo de domínio dentro do remote:
// ❌ incorreto no remote
{ path: 'users', children: [...] }Isso gera duplicação de prefixo e falha de match de rota em runtime.
-
ownership claro de domínio no Shell
-
rotas internas isoladas por MFE
-
composição previsível de URLs
-
suporte correto a deep linking
-
menor acoplamento entre MFEs
A prova de conceito evitou deliberadamente:
- Estado global compartilhado entre MFEs
- Orquestração avançada em runtime
- Estratégias de configuração multi-tenant
- Pipelines de CI/CD complexos
Esses pontos foram adiados para evitar complexidade prematura e manter o foco da PoC na validação arquitetural.
- Complexidade operacional maior em comparação a uma SPA monolítica
- Custo mais alto de onboarding para novos contribuidores
- Necessidade de governança explícita para evitar fragmentação
- Risco de overengineering caso a escala projetada não se concretize
Esses riscos foram conscientemente aceitos considerando o contexto institucional e a projeção de crescimento da plataforma.
Micro Frontends não devem ser escolhidos com base no tamanho atual do time, mas sim na trajetória esperada do sistema.
São apropriados quando há expectativa real de escalabilidade organizacional e evolução independente, mesmo com um time inicial reduzido. Devem ser evitados em aplicações que se espera permanecerem pequenas e estáveis ao longo do tempo.
Frontend Technical Lead
- Definição da arquitetura frontend
- Proposição e validação da adoção de Micro Frontends
- Seleção das tecnologias base
- Liderança das decisões arquiteturais e da estrutura do código
- Orientação de contribuidores juniores e distribuição de tarefas
- Tradução de uma proposta institucional de alto nível em decisões arquiteturais concretas de frontend
- As responsabilidades descritas refletem atuação técnica real no contexto desta prova de conceito arquitetural.
