Skip to content

📌 Guia Técnico para Arquitetura e Desenvolvimento de Micro-Frontend (MFE)

🔹 O que é um Micro-Frontend?

Um Micro-Frontend (MFE) é uma abordagem arquitetural que aplica ao front-end os mesmos princípios usados em microservices no back-end.

Em vez de manter uma única aplicação monolítica de interface, a aplicação é dividida em módulos menores, independentes e autônomos, que podem ser desenvolvidos, testados e implantados separadamente.

Cada MFE é responsável por uma parte específica do domínio de negócio e pode ser integrado a uma aplicação maior (geralmente chamada de Shell ou Container) através de técnicas como Webpack Module Federation, iframes ou Web Components.


🔹 Quais as principais vantagens?

Vantagem Descrição
Autonomia das equipes Cada squad pode trabalhar em um MFE específico sem bloquear outras equipes.
Deploy independente Atualizações podem ser entregues sem precisar publicar toda a aplicação.
Escalabilidade organizacional Facilita o crescimento da aplicação e do time de desenvolvimento.
Isolamento de falhas Erros em um MFE não necessariamente comprometem toda a aplicação.
Evolução tecnológica Diferentes MFEs podem adotar versões distintas de libs ou frameworks, permitindo evolução gradual.
Time-to-market mais rápido Novas funcionalidades chegam ao usuário sem depender de uma release centralizada.

🔹 Quais são os benefícios?

Benefício Descrição
Melhoria na manutenção Código mais enxuto e focado em um domínio específico reduz complexidade.
Reuso de componentes e padrões MFEs podem compartilhar bibliotecas (como o Design System) para manter consistência visual e de usabilidade.
Escalabilidade técnica Permite dividir a aplicação em múltiplos pipelines de build, testes e deploy.
Aumento da qualidade Com menor escopo de código em cada MFE, testes e revisões tornam-se mais eficazes.
Flexibilidade arquitetural Facilita a introdução de novas tecnologias sem reescrever toda a aplicação.
Alinhamento com microservices Cria simetria entre front-end e back-end, adotando práticas modernas de arquitetura distribuída.

🔹 Desenvolvimento:

Inicialização do Repositório

  • O projeto deverá ser inicializado a partir do Alquimia, sendo escolhida a categoria Static Front-End, com o template Micro-Frontend with Module Federation. Os seguintes parâmetros devem ser selecionados/informados:

Etapas - Alquimia

Parâmetro Valor
System finopping (platform-engineering)
GitHub Team 903 - Finopping
Component Name À escolher, desde que contenha um prefixo final nomeado como mfe
Repository GitHub Name Será inicializado com o prefixo plat-eng-finop e, composto pelo nome do componente inserido anteriormente.
Component Description Breve descrição do que se refere o componente (a ser criado)
  • Após, confirmar se todos os ambientes necessários serão apresentados. Caso não sejam, entrar em contato a partir do canal #fale-com-devops-cross.

  • Na sequência, deverão ser informados o sub-domínio e domínio - inclusive, será apresentado no Alqumia como o endereço de acesso ao MFE será consolidado.

  • Caso deseje associar algum canal do Slack específico, a próxima etapa será para isso.

  • E por fim, basta revisar e executar a criação do projeto.

Com o repositório criado, basta dar continuidade com as configurações básicas: Husky, Semantic Release, Template para Pull Requests, outros.

❗️ Dica importante para criar os commits...

Os commits semânticos são uma abordagens para rotular commits de uma forma mais significativa e estruturada, tornando o histórico de desenvolvimento de software mais fácil de entender e de se trabalhar. O objetivo principal dos commits semânticos é fornecer informações úteis sobre a natureza da mudança, o escopo das mudanças e o impacto que a mudança pode ter no sistema.

Tipo Emoji Quando Usar Versão
feat Quando adicionamos uma nova funcionalidade ao código ou ao projeto, sem mudanças significativas em funcionalidades existentes. Pode alterar completamente uma única funcionalidade. Não deve quebrar a compatibilidade com versões anteriores. Minor
fix 🐛 Quando corrigimos um bug ou um problema no código ou no projeto. Deve ser usado quando a correção não altera o comportamento de funcionalidades existentes. Não deve quebrar a compatibilidade com versões anteriores. Patch
perf Quando fazemos alterações que melhoram o desempenho do código ou do projeto ou adicionam melhorias a sistemas de monitoramento, logs, etc, de funcionalidades já existentes sem alterar o comportamento delas. Patch
style 💄 Quando fazemos alterações que não afetam o comportamento do código como alteração de textos estáticos ou alterações de estilos de componentes, etc. Patch
refactor 🔨 Quando fazemos alterações no código que não adicionam novas funcionalidades nem corrigem bugs, mas melhoram a estrutura, a legibilidade ou a manutenibilidade do código. Minor
chore 🚧 Quando fazemos alterações no código que não afetam o comportamento do projeto, como ajustes de configuração, atualizações de dependências, alteração de recursos de infraestrutura, etc. Patch
test 🧪 Quando adicionamos ou modificamos testes no código ou no projeto. n/a
docs 📚 Quando fazemos alterações na documentação do código ou do projeto, como README, guias, comentários, etc. n/a
ci 🔧 Quando fazemos alterações em configurações de integração contínua, como GitHub Actions. n/a

Estrutura de pastas e arquivos

Estrutura de Pastas e Arquivos

Uma das etapas mais importantes no desenvolvimento de um Micro-Frontend (MFE) é a definição de uma estrutura de pastas sólida, organizada e padronizada.
Essa estrutura serve como guia para o time, facilita o onboarding de novos desenvolvedores, aumenta a produtividade e reduz a chance de erros de manutenção ao longo do ciclo de vida do projeto.

No contexto da Plataforma Finopping, a padronização de diretórios também promove consistência entre todos os MFEs, facilitando integrações, automações de CI/CD e garantindo que práticas como testes, documentação e versionamento sejam seguidas de forma uniforme.

Quick Wins:

  • 🚀 Velocidade no desenvolvimento: menos tempo perdido procurando onde colocar ou encontrar código.
  • Qualidade de código: separação clara de responsabilidades reduz erros.
  • 📦 Reuso de lógica e componentes: hooks e serviços bem definidos podem ser aplicados em múltiplas partes do projeto.
  • 🔄 Testabilidade: com escopos menores (services, hooks, components), os testes ficam mais objetivos.
  • 🧩 Integração facilitada: quando todos os MFEs seguem a mesma estrutura, o Shell e outros times conseguem consumir e integrar módulos de forma previsível.
  • 📈 Escalabilidade futura: a base sólida permite crescimento do projeto sem gerar um novo “monólito disfarçado”.

Uma boa organização de pastas é essencial para manter a aplicação escalável, legível e fácil de dar manutenção. Cada seção descreve a finalidade da pasta/arquivo, o que deve conter e um espaço reservado para blocos de código de exemplo.

🧩 components/

Pasta dedicada a componentes visuais reutilizáveis dentro do escopo do MFE.
- Sempre baseados no Design System Flora.
- Não devem conter regras de negócio, apenas UI e interações.
- Componentes devem ser coesos e autocontidos, podendo ter subpastas próprias.

➡️ Exemplo de código:

    import { FloraButton } from "@grupoboticario/flora-react"

    type ButtonExampleProps = {
    name: string;
    action: () => void;
    }

    export const ButtonExample = ({ name, action }: ButtonExampleProps) => {
        return(
            <FloraButton
                    aria-label={name}
                    onClick={action}
                >
                    {name}
                </FloraButton>

        )
    }

🌐 contexts/

Armazena React Context Providers responsáveis por compartilhar estado em diferentes áreas do MFE. - Usado para dados de sessão, preferências ou estados que devem ser acessados por múltiplos componentes/páginas. - Evitar uso excessivo para não transformar tudo em estado global.

➡️ Exemplo de código:

    import { UsersService } from '../data/services/users.service';
    import { createContext, useMemo } from 'react';

    interface UsersProviderProps {
        children: React.ReactNode;
    }

    const UsersContext = createContext<UsersService | null>(null);

    function UsersProvider({ children }: Readonly<UsersProviderProps>) {
        const usersService = useMemo(() => new UsersService(), []);

        return (
            <UsersContext.Provider value={usersService}>
                {children}
            </UsersContext.Provider>
        );
    }

    export {
        UsersService,
        UsersProvider,
        UsersProviderProps,
    };

    export default UsersContext;

🗂️ data/models/

Contém tipos, interfaces e contratos de dados. - Representam entidades que vêm do back-end (API/BFF). - Servem como “contrato único de verdade” para a aplicação.

➡️ Exemplo de código:

    export interface IUser {
        id: string;
        name: string;
        email: string;
    }

📡 data/services/

Centraliza toda a lógica de integração com APIs/BFFs. - Nunca fazer chamadas HTTP direto em hooks ou componentes. - Utilizar um API Client configurado com interceptors (auth, tracing, error handling). - Cada arquivo deve corresponder a um domínio (ex.: users.service.ts).

➡️ Exemplo de código:

    import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
    import { IUser } from '../models/user.model';

    export interface IUsersService {
    get(): Promise<IUser[]>;
    }

    export class UsersService implements IUsersService {
    private readonly api: AxiosInstance;

    constructor() {
        this.api = axios.create({
        baseURL: 'https://jsonplaceholder.typicode.com',
        });
    }

    private async createConfig(): Promise<AxiosRequestConfig> {
        return {
        headers: {
            'Content-Type': 'application/json',
        },
        };
    }

    public async get(): Promise<IUser[]> {
        const config = await this.createConfig();
        const { data } = await this.api.get<IUser[]>('/users', config);
        return data;
    }

    public async getById(id: number): Promise<IUser> {
        const config = await this.createConfig();
        const { data } = await this.api.get<IUser>(`/users/${id}`, config);
        return data;
    }
    }

🔄 hooks/

Armazena React Hooks customizados para reuso de lógica. - Hooks de domínio: encapsulam regras específicas (ex.: useUsers). - Hooks técnicos: facilitam reuso de lógica (ex.: useDebounce). - Nomeação padrão: use-*.hook.ts.

➡️ Exemplo de código:

    import { useContext } from 'react';
    import { useQuery } from '@tanstack/react-query';
    import UsersContext from '../contexts/users.context';

    export function useUsers() {
    const users = useContext(UsersContext);

    return useQuery({
        queryKey: ['useUsers'],
        queryFn: () => users?.get() || null,
    });
    }

📄 pages/

Contém as páginas principais e rotas do MFE. - Cada página deve estar preparada para rodar standalone e também ser exposta via Module Federation. - Arquivo index.tsx dentro de pages/ deve exportar o Routes do domínio.

➡️ Exemplo de código:

    import { Flex, Spinner } from "@grupoboticario/flora-react";
    import { CardExample } from "../components/card-example"
    import { useUsers } from "../hooks/use-users.hook"

    export const PageExample = () => {
        const { isPending, isError, data, error } = useUsers();

        if (isPending) {
            return <Spinner />
        }

        if (isError) {
            return <div>Ocorreu um erro: {String(error)}</div>
        }

        return (
            <>
                <Flex direction='column' gap='$2'>
                    {data?.map(user => (
                        <CardExample key={user.id} id={user.id} name={user.name} email={user.email} />
                    ))}
                </Flex>
            </>
        )
    }

🔹 Testes Unitários, Cobertura e Reports

Objetivo

Garantir que cada MFE mantenha qualidade de código, previsibilidade de comportamento e segurança contra regressões através de testes unitários consistentes.

Ferramentas

  • Jest: framework principal de testes unitários.
  • React Testing Library (RTL): testes de componentes React, priorizando comportamento sobre implementação.
  • Coverage Reports: relatórios de cobertura para medir eficácia dos testes.

Cobertura Mínima

O projeto deve manter ≥ 80% de cobertura global em:

  • Statements (Declarações) Mede quantas instruções do código foram executadas pelos testes.
    Exemplo: um console.log() ou uma atribuição de variável. Se você tem 10 declarações e os testes passam por 8, sua cobertura de statements será 80%.

  • Branches (Ramificações) Mede se todos os caminhos de decisão foram testados.
    Exemplo: condicionais if/else, operadores ternários, switch/case. Se você testa apenas o if mas nunca o else, a cobertura de branches estará incompleta.

  • Functions (Funções) Mede quantas funções ou métodos foram chamadas pelos testes.
    Exemplo: se um módulo possui 5 funções exportadas mas só 3 foram chamadas nos testes, a cobertura de functions será 60%.

  • Lines (Linhas) Mede a quantidade de linhas de código executadas durante os testes.
    É parecido com statements, mas contado de forma mais granular.
    Exemplo: se um arquivo tem 100 linhas e 90 são executadas nos testes, a cobertura de lines será 90%.

Localização dos Testes

  • Os testes devem ser mantidos próximos aos arquivos que validam ou dentro de uma pasta __tests__.
  • Extensão recomendada:
  • *.test.ts
  • *.test.tsx

Relatórios

  • HTML (lcov-report): usado localmente para consulta rápida.
  • Cobertura XML (cobertura/junit): usado em pipelines de CI/CD para integração com ferramentas como SonarQube ou Codecov.

Exemplo de script em package.json

```json { "scripts": { "test": "jest --passWithNoTests", "test:cov": "jest --coverage", "test:ci": "jest --ci --reporters=default --reporters=jest-junit --coverage" } }