---
title: "Typed Contract Pipeline (TCP) - DevX e escala real em Microfrontends"
slug: typed-contract-pipeline-tcp-microfrontends
date: 2025-12-21
category: "Arquitetura"
tags: ["microfrontends", "single-spa", "importmap", "typescript", "devx", "typed-contracts", "plataformizacao", "arquitetura-frontend"]
readTime: "12 min"
excerpt: "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."
url: https://eliseu.dev/blog/typed-contract-pipeline-tcp-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.

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:

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

O time A consome via importmap:

```html
<script type="importmap">
{
  "imports": {
    "@org/design-system": "https://cdn.example.com/ds@1.2.3/index.js"
  }
}
</script>
```

E usa em TypeScript:

```ts
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:

```ts
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/design-system@1.3.0`.

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:

```ts
<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


<div class="mermaid-prerendered">
<svg id="mermaid-svg" width="100%" xmlns="http://www.w3.org/2000/svg" class="flowchart" style="max-width: 602.9375px;" viewBox="0 0 602.9375 502" role="graphics-document document" aria-roledescription="flowchart-v2" xmlns:xlink="http://www.w3.org/1999/xlink"><style xmlns="http://www.w3.org/1999/xhtml">@import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css");</style><style>#mermaid-svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#ccc;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg .error-icon{fill:#a44141;}#mermaid-svg .error-text{fill:#ddd;stroke:#ddd;}#mermaid-svg .edge-thickness-normal{stroke-width:1px;}#mermaid-svg .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg .marker{fill:lightgrey;stroke:lightgrey;}#mermaid-svg .marker.cross{stroke:lightgrey;}#mermaid-svg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg p{margin:0;}#mermaid-svg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#ccc;}#mermaid-svg .cluster-label text{fill:#F9FFFE;}#mermaid-svg .cluster-label span{color:#F9FFFE;}#mermaid-svg .cluster-label span p{background-color:transparent;}#mermaid-svg .label text,#mermaid-svg span{fill:#ccc;color:#ccc;}#mermaid-svg .node rect,#mermaid-svg .node circle,#mermaid-svg .node ellipse,#mermaid-svg .node polygon,#mermaid-svg .node path{fill:#1f2020;stroke:#ccc;stroke-width:1px;}#mermaid-svg .rough-node .label text,#mermaid-svg .node .label text,#mermaid-svg .image-shape .label,#mermaid-svg .icon-shape .label{text-anchor:middle;}#mermaid-svg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg .rough-node .label,#mermaid-svg .node .label,#mermaid-svg .image-shape .label,#mermaid-svg .icon-shape .label{text-align:center;}#mermaid-svg .node.clickable{cursor:pointer;}#mermaid-svg .root .anchor path{fill:lightgrey!important;stroke-width:0;stroke:lightgrey;}#mermaid-svg .arrowheadPath{fill:lightgrey;}#mermaid-svg .edgePath .path{stroke:lightgrey;stroke-width:2.0px;}#mermaid-svg .flowchart-link{stroke:lightgrey;fill:none;}#mermaid-svg .edgeLabel{background-color:hsl(0, 0%, 34.4117647059%);text-align:center;}#mermaid-svg .edgeLabel p{background-color:hsl(0, 0%, 34.4117647059%);}#mermaid-svg .edgeLabel rect{opacity:0.5;background-color:hsl(0, 0%, 34.4117647059%);fill:hsl(0, 0%, 34.4117647059%);}#mermaid-svg .labelBkg{background-color:rgba(87.75, 87.75, 87.75, 0.5);}#mermaid-svg .cluster rect{fill:hsl(180, 1.5873015873%, 28.3529411765%);stroke:rgba(255, 255, 255, 0.25);stroke-width:1px;}#mermaid-svg .cluster text{fill:#F9FFFE;}#mermaid-svg .cluster span{color:#F9FFFE;}#mermaid-svg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(20, 1.5873015873%, 12.3529411765%);border:1px solid rgba(255, 255, 255, 0.25);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#ccc;}#mermaid-svg rect.text{fill:none;stroke-width:0;}#mermaid-svg .icon-shape,#mermaid-svg .image-shape{background-color:hsl(0, 0%, 34.4117647059%);text-align:center;}#mermaid-svg .icon-shape p,#mermaid-svg .image-shape p{background-color:hsl(0, 0%, 34.4117647059%);padding:2px;}#mermaid-svg .icon-shape rect,#mermaid-svg .image-shape rect{opacity:0.5;background-color:hsl(0, 0%, 34.4117647059%);fill:hsl(0, 0%, 34.4117647059%);}#mermaid-svg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}</style><g><marker id="mermaid-svg_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="mermaid-svg_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="mermaid-svg_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="mermaid-svg_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="mermaid-svg_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"/></marker><marker id="mermaid-svg_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"/></marker><g class="root"><g class="clusters"/><g class="edgePaths"><path d="M301.469,86L301.469,92.167C301.469,98.333,301.469,110.667,301.469,122.333C301.469,134,301.469,145,301.469,150.5L301.469,156" id="L_A_B_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_A_B_0" data-points="W3sieCI6MzAxLjQ2ODc1LCJ5Ijo4Nn0seyJ4IjozMDEuNDY4NzUsInkiOjEyM30seyJ4IjozMDEuNDY4NzUsInkiOjE2MH1d" marker-end="url(#mermaid-svg_flowchart-v2-pointEnd)"/><path d="M301.469,214L301.469,220.167C301.469,226.333,301.469,238.667,301.469,250.333C301.469,262,301.469,273,301.469,278.5L301.469,284" id="L_B_C_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_B_C_0" data-points="W3sieCI6MzAxLjQ2ODc1LCJ5IjoyMTR9LHsieCI6MzAxLjQ2ODc1LCJ5IjoyNTF9LHsieCI6MzAxLjQ2ODc1LCJ5IjoyODh9XQ==" marker-end="url(#mermaid-svg_flowchart-v2-pointEnd)"/><path d="M197.65,366L181.235,372.167C164.819,378.333,131.988,390.667,115.572,402.333C99.156,414,99.156,425,99.156,430.5L99.156,436" id="L_C_D_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_C_D_0" data-points="W3sieCI6MTk3LjY1MDQ5MzQyMTA1MjYzLCJ5IjozNjZ9LHsieCI6OTkuMTU2MjUsInkiOjQwM30seyJ4Ijo5OS4xNTYyNSwieSI6NDQwfV0=" marker-end="url(#mermaid-svg_flowchart-v2-pointEnd)"/><path d="M301.469,366L301.469,372.167C301.469,378.333,301.469,390.667,301.469,402.333C301.469,414,301.469,425,301.469,430.5L301.469,436" id="L_C_E_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_C_E_0" data-points="W3sieCI6MzAxLjQ2ODc1LCJ5IjozNjZ9LHsieCI6MzAxLjQ2ODc1LCJ5Ijo0MDN9LHsieCI6MzAxLjQ2ODc1LCJ5Ijo0NDB9XQ==" marker-end="url(#mermaid-svg_flowchart-v2-pointEnd)"/><path d="M405.287,366L421.703,372.167C438.118,378.333,470.95,390.667,487.366,402.333C503.781,414,503.781,425,503.781,430.5L503.781,436" id="L_C_F_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_C_F_0" data-points="W3sieCI6NDA1LjI4NzAwNjU3ODk0NzQsInkiOjM2Nn0seyJ4Ijo1MDMuNzgxMjUsInkiOjQwM30seyJ4Ijo1MDMuNzgxMjUsInkiOjQ0MH1d" marker-end="url(#mermaid-svg_flowchart-v2-pointEnd)"/></g><g class="edgeLabels"><g class="edgeLabel" transform="translate(301.46875, 123)"><g class="label" data-id="L_A_B_0" transform="translate(-38.6875, -12)"><foreignObject width="77.375" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel "><p>extrai tipos</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(301.46875, 251)"><g class="label" data-id="L_B_C_0" transform="translate(-25.3515625, -12)"><foreignObject width="50.703125" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel "><p>publica</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(99.15625, 403)"><g class="label" data-id="L_C_D_0" transform="translate(-91.15625, -12)"><foreignObject width="182.3125" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel "><p>distribui automaticamente</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(301.46875, 403)"><g class="label" data-id="L_C_E_0" transform="translate(-91.15625, -12)"><foreignObject width="182.3125" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel "><p>distribui automaticamente</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(503.78125, 403)"><g class="label" data-id="L_C_F_0" transform="translate(-91.15625, -12)"><foreignObject width="182.3125" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel "><p>distribui automaticamente</p></span></div></foreignObject></g></g></g><g class="nodes"><g class="node default  " id="flowchart-A-0" transform="translate(301.46875, 47)"><rect class="basic label-container" style="fill:#1e293b !important;stroke:#0ea5e9 !important;stroke-width:2px !important" x="-130" y="-39" width="260" height="78"/><g class="label" style="color:#fff !important" transform="translate(-100, -24)"><rect/><foreignObject width="200" height="48"><div style="color: rgb(255, 255, 255) !important; display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel "><p>Design System\n@org/ds@1.3.0</p></span></div></foreignObject></g></g><g class="node default  " id="flowchart-B-1" transform="translate(301.46875, 187)"><rect class="basic label-container" style="fill:#1e293b !important;stroke:#0ea5e9 !important;stroke-width:2px !important" x="-104.9921875" y="-27" width="209.984375" height="54"/><g class="label" style="color:#fff !important" transform="translate(-74.9921875, -12)"><rect/><foreignObject width="149.984375" height="24"><div style="color: rgb(255, 255, 255) !important; display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel "><p>TCP Pipeline\nCI/CD</p></span></div></foreignObject></g></g><g class="node default  " id="flowchart-C-3" transform="translate(301.46875, 327)"><rect class="basic label-container" style="fill:#1e293b !important;stroke:#0ea5e9 !important;stroke-width:2px !important" x="-130" y="-39" width="260" height="78"/><g class="label" style="color:#fff !important" transform="translate(-100, -24)"><rect/><foreignObject width="200" height="48"><div style="color: rgb(255, 255, 255) !important; display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel "><p>@org/platform-types\n@1.3.0</p></span></div></foreignObject></g></g><g class="node default  " id="flowchart-D-5" transform="translate(99.15625, 467)"><rect class="basic label-container" style="fill:#1e293b !important;stroke:#10b981 !important;stroke-width:2px !important" x="-54.6015625" y="-27" width="109.203125" height="54"/><g class="label" style="color:#fff !important" transform="translate(-24.6015625, -12)"><rect/><foreignObject width="49.203125" height="24"><div style="color: rgb(255, 255, 255) !important; display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel "><p>Time A</p></span></div></foreignObject></g></g><g class="node default  " id="flowchart-E-7" transform="translate(301.46875, 467)"><rect class="basic label-container" style="fill:#1e293b !important;stroke:#10b981 !important;stroke-width:2px !important" x="-55.0390625" y="-27" width="110.078125" height="54"/><g class="label" style="color:#fff !important" transform="translate(-25.0390625, -12)"><rect/><foreignObject width="50.078125" height="24"><div style="color: rgb(255, 255, 255) !important; display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel "><p>Time B</p></span></div></foreignObject></g></g><g class="node default  " id="flowchart-F-9" transform="translate(503.78125, 467)"><rect class="basic label-container" style="fill:#1e293b !important;stroke:#10b981 !important;stroke-width:2px !important" x="-55.484375" y="-27" width="110.96875" height="54"/><g class="label" style="color:#fff !important" transform="translate(-25.484375, -12)"><rect/><foreignObject width="50.96875" height="24"><div style="color: rgb(255, 255, 255) !important; display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel "><p>Time C</p></span></div></foreignObject></g></g></g></g></g></svg>
</div>


### Implementação prática

**1. Extração automática de contratos**

```ts
// 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**

```json
// 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**

```yaml
# .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**

```json
// apps/time-a/package.json
{
  "devDependencies": {
    "@org/platform-types": "^1.3.0"
  }
}
```

```ts
// 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


<div class="mermaid-prerendered">
<svg id="mermaid-svg" width="100%" xmlns="http://www.w3.org/2000/svg" class="flowchart" style="max-width: 991.75px;" viewBox="0 0 991.75 362" role="graphics-document document" aria-roledescription="flowchart-v2" xmlns:xlink="http://www.w3.org/1999/xlink"><style xmlns="http://www.w3.org/1999/xhtml">@import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css");</style><style>#mermaid-svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#ccc;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg .error-icon{fill:#a44141;}#mermaid-svg .error-text{fill:#ddd;stroke:#ddd;}#mermaid-svg .edge-thickness-normal{stroke-width:1px;}#mermaid-svg .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg .marker{fill:lightgrey;stroke:lightgrey;}#mermaid-svg .marker.cross{stroke:lightgrey;}#mermaid-svg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg p{margin:0;}#mermaid-svg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#ccc;}#mermaid-svg .cluster-label text{fill:#F9FFFE;}#mermaid-svg .cluster-label span{color:#F9FFFE;}#mermaid-svg .cluster-label span p{background-color:transparent;}#mermaid-svg .label text,#mermaid-svg span{fill:#ccc;color:#ccc;}#mermaid-svg .node rect,#mermaid-svg .node circle,#mermaid-svg .node ellipse,#mermaid-svg .node polygon,#mermaid-svg .node path{fill:#1f2020;stroke:#ccc;stroke-width:1px;}#mermaid-svg .rough-node .label text,#mermaid-svg .node .label text,#mermaid-svg .image-shape .label,#mermaid-svg .icon-shape .label{text-anchor:middle;}#mermaid-svg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg .rough-node .label,#mermaid-svg .node .label,#mermaid-svg .image-shape .label,#mermaid-svg .icon-shape .label{text-align:center;}#mermaid-svg .node.clickable{cursor:pointer;}#mermaid-svg .root .anchor path{fill:lightgrey!important;stroke-width:0;stroke:lightgrey;}#mermaid-svg .arrowheadPath{fill:lightgrey;}#mermaid-svg .edgePath .path{stroke:lightgrey;stroke-width:2.0px;}#mermaid-svg .flowchart-link{stroke:lightgrey;fill:none;}#mermaid-svg .edgeLabel{background-color:hsl(0, 0%, 34.4117647059%);text-align:center;}#mermaid-svg .edgeLabel p{background-color:hsl(0, 0%, 34.4117647059%);}#mermaid-svg .edgeLabel rect{opacity:0.5;background-color:hsl(0, 0%, 34.4117647059%);fill:hsl(0, 0%, 34.4117647059%);}#mermaid-svg .labelBkg{background-color:rgba(87.75, 87.75, 87.75, 0.5);}#mermaid-svg .cluster rect{fill:hsl(180, 1.5873015873%, 28.3529411765%);stroke:rgba(255, 255, 255, 0.25);stroke-width:1px;}#mermaid-svg .cluster text{fill:#F9FFFE;}#mermaid-svg .cluster span{color:#F9FFFE;}#mermaid-svg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(20, 1.5873015873%, 12.3529411765%);border:1px solid rgba(255, 255, 255, 0.25);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#ccc;}#mermaid-svg rect.text{fill:none;stroke-width:0;}#mermaid-svg .icon-shape,#mermaid-svg .image-shape{background-color:hsl(0, 0%, 34.4117647059%);text-align:center;}#mermaid-svg .icon-shape p,#mermaid-svg .image-shape p{background-color:hsl(0, 0%, 34.4117647059%);padding:2px;}#mermaid-svg .icon-shape rect,#mermaid-svg .image-shape rect{opacity:0.5;background-color:hsl(0, 0%, 34.4117647059%);fill:hsl(0, 0%, 34.4117647059%);}#mermaid-svg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}</style><g><marker id="mermaid-svg_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="mermaid-svg_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="mermaid-svg_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="mermaid-svg_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="mermaid-svg_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"/></marker><marker id="mermaid-svg_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"/></marker><g class="root"><g class="clusters"/><g class="edgePaths"><path d="M495.875,132L495.875,138.167C495.875,144.333,495.875,156.667,495.875,168.333C495.875,180,495.875,191,495.875,196.5L495.875,202" id="L_CT_RT_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_CT_RT_0" data-points="W3sieCI6NDk1Ljg3NSwieSI6MTMyfSx7IngiOjQ5NS44NzUsInkiOjE2OX0seyJ4Ijo0OTUuODc1LCJ5IjoyMDZ9XQ==" marker-end="url(#mermaid-svg_flowchart-v2-pointEnd)"/></g><g class="edgeLabels"><g class="edgeLabel" transform="translate(495.875, 169)"><g class="label" data-id="L_CT_RT_0" transform="translate(-60.0390625, -12)"><foreignObject width="120.078125" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel "><p>desenvolvimento</p></span></div></foreignObject></g></g></g><g class="nodes"><g class="root" transform="translate(0, 198)"><g class="clusters"><g class="cluster " id="RT" data-look="classic"><rect style="fill:#1e293b !important;stroke:#10b981 !important;stroke-width:2px !important" x="8" y="8" width="975.75" height="148"/><g class="cluster-label " transform="translate(429.1953125, 8)"><foreignObject width="133.359375" height="24"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel " style="color:#fff !important"><p>Runtime (Browser)</p></span></div></foreignObject></g></g></g><g class="edgePaths"><path d="M305.5,82L311.75,82C318,82,330.5,82,342.333,82C354.167,82,365.333,82,370.917,82L376.5,82" id="L_D_E_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_D_E_0" data-points="W3sieCI6MzA1LjUsInkiOjgyfSx7IngiOjM0MywieSI6ODJ9LHsieCI6MzgwLjUsInkiOjgyfV0=" marker-end="url(#mermaid-svg_flowchart-v2-pointEnd)"/><path d="M611.25,82L617.5,82C623.75,82,636.25,82,648.083,82C659.917,82,671.083,82,676.667,82L682.25,82" id="L_E_F_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_E_F_0" data-points="W3sieCI6NjExLjI1LCJ5Ijo4Mn0seyJ4Ijo2NDguNzUsInkiOjgyfSx7IngiOjY4Ni4yNSwieSI6ODJ9XQ==" marker-end="url(#mermaid-svg_flowchart-v2-pointEnd)"/></g><g class="edgeLabels"><g class="edgeLabel"><g class="label" data-id="L_D_E_0" transform="translate(0, 0)"><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel "></span></div></foreignObject></g></g><g class="edgeLabel"><g class="label" data-id="L_E_F_0" transform="translate(0, 0)"><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel "></span></div></foreignObject></g></g></g><g class="nodes"><g class="node default  " id="flowchart-D-7" transform="translate(175.5, 82)"><rect class="basic label-container" style="fill:#334155 !important;stroke:#10b981 !important;stroke-width:2px !important" x="-130" y="-39" width="260" height="78"/><g class="label" style="color:#fff !important" transform="translate(-100, -24)"><rect/><foreignObject width="200" height="48"><div style="color: rgb(255, 255, 255) !important; display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel "><p>importmap → @org/design-system@1.2.3</p></span></div></foreignObject></g></g><g class="node default  " id="flowchart-E-8" transform="translate(495.875, 82)"><rect class="basic label-container" style="fill:#334155 !important;stroke:#10b981 !important;stroke-width:2px !important" x="-115.375" y="-27" width="230.75" height="54"/><g class="label" style="color:#fff !important" transform="translate(-85.375, -12)"><rect/><foreignObject width="170.75" height="24"><div style="color: rgb(255, 255, 255) !important; display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel "><p>Carregamento dinâmico</p></span></div></foreignObject></g></g><g class="node default  " id="flowchart-F-9" transform="translate(816.25, 82)"><rect class="basic label-container" style="fill:#334155 !important;stroke:#10b981 !important;stroke-width:2px !important" x="-130" y="-39" width="260" height="78"/><g class="label" style="color:#fff !important" transform="translate(-100, -24)"><rect/><foreignObject width="200" height="48"><div style="color: rgb(255, 255, 255) !important; display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel "><p>Versão controlada remotamente</p></span></div></foreignObject></g></g></g></g><g class="root" transform="translate(15.7890625, 0)"><g class="clusters"><g class="cluster " id="CT" data-look="classic"><rect style="fill:#1e293b !important;stroke:#0ea5e9 !important;stroke-width:2px !important" x="8" y="8" width="944.171875" height="124"/><g class="cluster-label " transform="translate(385.25, 8)"><foreignObject width="189.671875" height="24"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel " style="color:#fff !important"><p>Compile-Time (TypeScript)</p></span></div></foreignObject></g></g></g><g class="edgePaths"><path d="M305.172,70L311.422,70C317.672,70,330.172,70,342.005,70C353.839,70,365.005,70,370.589,70L376.172,70" id="L_A_B_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_A_B_0" data-points="W3sieCI6MzA1LjE3MTg3NSwieSI6NzB9LHsieCI6MzQyLjY3MTg3NSwieSI6NzB9LHsieCI6MzgwLjE3MTg3NSwieSI6NzB9XQ==" marker-end="url(#mermaid-svg_flowchart-v2-pointEnd)"/><path d="M593.156,70L599.406,70C605.656,70,618.156,70,629.99,70C641.823,70,652.99,70,658.573,70L664.156,70" id="L_B_C_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_B_C_0" data-points="W3sieCI6NTkzLjE1NjI1LCJ5Ijo3MH0seyJ4Ijo2MzAuNjU2MjUsInkiOjcwfSx7IngiOjY2OC4xNTYyNSwieSI6NzB9XQ==" marker-end="url(#mermaid-svg_flowchart-v2-pointEnd)"/></g><g class="edgeLabels"><g class="edgeLabel"><g class="label" data-id="L_A_B_0" transform="translate(0, 0)"><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel "></span></div></foreignObject></g></g><g class="edgeLabel"><g class="label" data-id="L_B_C_0" transform="translate(0, 0)"><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel "></span></div></foreignObject></g></g></g><g class="nodes"><g class="node default  " id="flowchart-A-0" transform="translate(175.3359375, 70)"><rect class="basic label-container" style="fill:#334155 !important;stroke:#0ea5e9 !important;stroke-width:2px !important" x="-129.8359375" y="-27" width="259.671875" height="54"/><g class="label" style="color:#fff !important" transform="translate(-99.8359375, -12)"><rect/><foreignObject width="199.671875" height="24"><div style="color: rgb(255, 255, 255) !important; display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel "><p>@org/platform-types@latest</p></span></div></foreignObject></g></g><g class="node default  " id="flowchart-B-1" transform="translate(486.6640625, 70)"><rect class="basic label-container" style="fill:#334155 !important;stroke:#0ea5e9 !important;stroke-width:2px !important" x="-106.4921875" y="-27" width="212.984375" height="54"/><g class="label" style="color:#fff !important" transform="translate(-76.4921875, -12)"><rect/><foreignObject width="152.984375" height="24"><div style="color: rgb(255, 255, 255) !important; display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel "><p>Type safety garantida</p></span></div></foreignObject></g></g><g class="node default  " id="flowchart-C-2" transform="translate(791.4140625, 70)"><rect class="basic label-container" style="fill:#334155 !important;stroke:#0ea5e9 !important;stroke-width:2px !important" x="-123.2578125" y="-27" width="246.515625" height="54"/><g class="label" style="color:#fff !important" transform="translate(-93.2578125, -12)"><rect/><foreignObject width="186.515625" height="24"><div style="color: rgb(255, 255, 255) !important; display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel "><p>Validação antes do deploy</p></span></div></foreignObject></g></g></g></g></g></g></g></svg>
</div>


### 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/platform-types@1.3.0` é 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.