Voltar ao Blog
    Arquitetura

    Typed Contract Pipeline (TCP) - DevX e escala real em Microfrontends

    Microfrontends não falham por falta de tecnologia. Eles falham quando a experiência de desenvolvimento (DevX) se deteriora à medida que o sistema cresce. Este artigo apresenta o Typed Contract Pipeline (TCP), uma abordagem de plataforma aplicada a arquiteturas de Microfrontends com single-spa + importmap, com foco em DevX e escala organizacional.

    21 de dezembro de 202512 min

    Microfrontends não falham por falta de tecnologia.
    Eles falham quando a experiência de desenvolvimento (DevX) se deteriora à medida que o sistema cresce.

    No início, a arquitetura entrega o que promete:

    • times independentes
    • deploy desacoplado
    • ownership claro

    Com o tempo, surgem problemas recorrentes:

    • contratos quebrando silenciosamente
    • dependências internas se multiplicando
    • circularidade entre libs
    • atualizações manuais constantes
    • erros descobertos apenas em produção
    • dificuldade para rastrear jornadas do usuário

    Este artigo apresenta uma abordagem de plataforma, não de ferramenta:
    o Typed Contract Pipeline (TCP), aplicado a arquiteturas de Microfrontends com single-spa + importmap, com foco em DevX e escala organizacional.

    Importmaps resolvem runtime, não contratos

    Importmaps são excelentes para:

    • controlar versões remotamente
    • evitar rebuilds globais
    • permitir deploy independente

    Mas importmaps não carregam tipagem.
    Eles resolvem runtime, não integração.

    Em arquiteturas distribuídas, isso cria um risco estrutural:

    mudanças compatíveis em runtime podem ser incompatíveis em integração — e o TypeScript não vê.

    Tipos não são detalhe. São contratos.

    Em microfrontends:

    • props são contratos
    • eventos são contratos
    • hooks expostos são contratos
    • contextos compartilhados são contratos

    Se esses contratos não forem:

    • explícitos
    • versionados
    • distribuídos automaticamente

    o sistema escala em número de times, mas não em confiança.

    O que é o Typed Contract Pipeline (TCP)

    O Typed Contract Pipeline trata contratos tipados como artefatos de primeira classe da plataforma.

    Definição objetiva

    Typed Contract Pipeline é um pipeline automatizado que extrai contratos tipados dos componentes centrais da plataforma, agrega esses contratos em um pacote único e governado (@org/platform-types) e os distribui automaticamente para todos os consumidores, garantindo segurança de tipos em tempo de desenvolvimento — independente do runtime.

    Princípios centrais:

    • runtime e compile-time são tratados separadamente
    • contratos são públicos e explícitos
    • automação substitui coordenação manual

    Um cenário comum (e silenciosamente perigoso)

    Imagine um Design System compartilhado.

    Versão atual:

    export interface ButtonProps {
      variant: "primary" | "secondary";
      size: "sm" | "md";
    }
    

    O time A consome via importmap:

    <script type="importmap">
    {
      "imports": {
        "@org/design-system": "https://cdn.example.com/[email protected]/index.js"
      }
    }
    </script>
    

    E usa em TypeScript:

    import { Button } from '@org/design-system';
    
    <Button variant="primary" size="sm" />
    

    Tudo funciona. O TypeScript valida, o runtime carrega.

    O problema silencioso

    Duas semanas depois, o time do Design System adiciona uma nova prop:

    export interface ButtonProps {
      variant: "primary" | "secondary" | "tertiary"; // nova opção
      size: "sm" | "md" | "lg"; // nova opção
      loading?: boolean; // nova prop opcional
    }
    

    E publica @org/[email protected].

    O importmap do time A ainda aponta para 1.2.3.
    O TypeScript do time A ainda vê os tipos de 1.2.3 (se tiverem instalado localmente).
    O runtime carrega 1.2.3.

    Tudo continua funcionando.

    Mas o time B atualiza o importmap para 1.3.0 e começa a usar:

    <Button variant="tertiary" size="lg" loading={true} />
    

    O time A, sem saber, tenta usar variant="tertiary" baseado em documentação desatualizada ou comunicação verbal.
    O TypeScript do time A não reclama (porque os tipos locais ainda são 1.2.3).
    O runtime do time A não reclama (porque o importmap ainda aponta para 1.2.3).

    Em desenvolvimento, tudo parece OK.

    Em produção, quando o importmap é atualizado para 1.3.0 sem aviso, o código do time A quebra silenciosamente — porque variant="tertiary" não existe em 1.2.3, mas o TypeScript nunca avisou.

    Por que isso acontece

    1. Tipos locais desincronizados: Cada time pode ter uma versão diferente dos tipos instalada localmente.
    2. Importmap independente: O importmap controla o runtime, mas não sincroniza tipos.
    3. Falta de visibilidade: Ninguém vê que o time A está usando uma versão antiga até quebrar.
    4. Coordenação manual: Depende de comunicação humana para sincronizar versões.

    Como o TCP resolve

    O Typed Contract Pipeline automatiza a extração, agregação e distribuição de contratos tipados.

    Fluxo do TCP

    extrai tipos

    publica

    distribui automaticamente

    distribui automaticamente

    distribui automaticamente

    Design System\n@org/[email protected]

    TCP Pipeline\nCI/CD

    @org/platform-types\[email protected]

    Time A

    Time B

    Time C

    Implementação prática

    1. Extração automática de contratos

    // scripts/extract-contracts.ts
    import { extractTypes } from '@tcp/extractor';
    import { writeFileSync } from 'fs';
    
    const contracts = extractTypes({
      packages: [
        '@org/design-system',
        '@org/shared-hooks',
        '@org/event-bus'
      ],
      output: './dist/contracts.d.ts'
    });
    
    writeFileSync('./dist/contracts.d.ts', contracts);
    

    2. Agregação em pacote único

    // packages/platform-types/package.json
    {
      "name": "@org/platform-types",
      "version": "1.3.0",
      "types": "./index.d.ts",
      "exports": {
        "./design-system": "./design-system.d.ts",
        "./hooks": "./hooks.d.ts",
        "./events": "./events.d.ts"
      }
    }
    

    3. Distribuição automática

    # .github/workflows/tcp-pipeline.yml
    name: TCP Pipeline
    
    on:
      push:
        branches: [main]
        paths:
          - 'packages/design-system/**'
          - 'packages/shared-hooks/**'
    
    jobs:
      extract-and-publish:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v3
          - uses: actions/setup-node@v3
          
          - name: Extract contracts
            run: npm run extract-contracts
          
          - name: Publish @org/platform-types
            run: npm publish --access public
          
          - name: Notify teams
            run: npm run notify-teams
    

    4. Consumo automático nos times

    // apps/time-a/package.json
    {
      "devDependencies": {
        "@org/platform-types": "^1.3.0"
      }
    }
    
    // apps/time-a/src/components/MyButton.tsx
    import type { ButtonProps } from '@org/platform-types/design-system';
    
    // TypeScript agora sempre vê a versão mais recente
    const MyButton = (props: ButtonProps) => {
      // Se tentar usar 'tertiary' e a versão local for 1.2.3,
      // TypeScript reclama ANTES de ir para produção
    };
    

    Runtime federation with compile-time safety

    O TCP separa runtime federation (importmap) de compile-time safety (tipos).

    Arquitetura híbrida

    desenvolvimento

    Runtime (Browser)

    importmap → @org/[email protected]

    Carregamento dinâmico

    Versão controlada remotamente

    Compile-Time (TypeScript)

    @org/platform-types@latest

    Type safety garantida

    Validação antes do deploy

    Benefícios da separação

    1. TypeScript sempre atualizado: @org/platform-types é atualizado automaticamente via TCP.
    2. Runtime controlado: Importmap permite controle de versão em produção sem rebuild.
    3. Detecção precoce: Incompatibilidades são detectadas em desenvolvimento, não em produção.
    4. Coordenação zero: Times não precisam se comunicar sobre mudanças de tipos.

    Exemplo prático

    Cenário: Design System adiciona variant="tertiary".

    Com TCP:

    1. Pipeline extrai tipos automaticamente.
    2. @org/[email protected] é publicado.
    3. Time A atualiza dependência: npm install @org/platform-types@latest.
    4. TypeScript agora vê variant="tertiary" como válido.
    5. Se o time A tentar usar variant="tertiary" mas o importmap ainda apontar para 1.2.3, o TypeScript não reclama (porque os tipos são compatíveis para frente).
    6. Quando o importmap for atualizado para 1.3.0, tudo funciona.

    Sem TCP:

    1. Time A não sabe que variant="tertiary" existe.
    2. TypeScript não valida porque tipos locais são 1.2.3.
    3. Time A pode tentar usar variant="tertiary" baseado em documentação.
    4. Em produção, quando importmap atualiza, código quebra.

    Conclusão

    Microfrontends escalam quando:

    • runtime é desacoplado
    • contratos são explícitos
    • automação substitui coordenação
    • DevX é tratado como produto

    O Typed Contract Pipeline não é uma ferramenta.
    É uma mudança de mentalidade.

    Autonomia sem contrato é caos.
    Contrato sem automação é burocracia.
    TCP resolve os dois.