02 - Customer-Supplier
video: https://youtu.be/... no frontmatter da nota.Customer-Supplier
Customer-Supplier é o padrão de integração entre contextos que organiza a relação em torno de dois papéis explícitos: o upstream, que fornece o serviço ou dado, e o downstream, que consome e depende do que é fornecido. O diferencial do padrão não está na troca técnica, e sim no acordo social que a acompanha: o fornecedor trata as expectativas do consumidor como requisito de teste automatizado, e o consumidor tem canal formal para negociar prioridades. Em produção, essa estrutura resolve o problema mais comum de integração interna, que é o time consumidor depender de um time fornecedor sem qualquer mecanismo de accountability. A relação informal funciona bem até o primeiro conflito de prioridade, quando a necessidade do consumidor compete com o roadmap do fornecedor e perde silenciosamente. Com o padrão aplicado, a expectativa vira contrato testado e a prioridade vira negociação registrada. O resultado é uma integração que sobrevive a trocas de pessoas, mudanças de escopo e picos de carga.
Papéis de upstream e downstream
O upstream fornece a capacidade, e o downstream consome. O nome técnico do padrão descreve a direção da dependência: o downstream depende do upstream, nunca o contrário, e essa assimetria é a base de todo o resto.
Os elementos do acordo, em um desenho maduro, são:
- Contrato formal, em OpenAPI, gRPC ou schema de evento, publicado e versionado pelo upstream como artefato de primeira classe.
- Testes automatizados de expectativa, em que o conjunto de necessidades do downstream roda contra o pipeline do upstream, bloqueando mudanças que quebrem o consumidor.
- Canal de negociação, normalmente revisão periódica registrada, em que o downstream exerce influência real sobre o roadmap do upstream.
- SLA de disponibilidade e de resposta a incidentes, porque a falha do upstream se torna diretamente a falha do downstream.
- Política de compatibilidade, que define o que o upstream pode mudar sem avisar e o que exige janela de transição.
A configuração de poder entre os dois lados varia e o padrão a tolera. Quando o downstream é produto de receita e o upstream é serviço de apoio, o consumidor tem peso de prioridade maior. Quando o upstream é plataforma corporativa que atende dezenas de consumidores, o poder se inverte, e o downstream passa a se adaptar dentro das extensões que a plataforma oferece. O padrão funciona nos dois casos, desde que os papéis e as expectativas sejam declarados.
| Configuração de poder | Exemplo típico | Efeito prático no acordo |
|---|---|---|
| Downstream dominante | Checkout sobre serviço de apoio interno | Demandas do consumidor entram no topo do backlog upstream |
| Upstream dominante | Plataforma corporativa com dezenas de consumidores | Influência via extensões e via comitê de consumidores |
| Poder equilibrado | Dois produtos da mesma diretoria | Negociação por troca de valor em ambas as direções |
| Upstream externo | Provedor SaaS contratado | Padrão degrada para Conformist ou ACL, com contratos de nível de serviço |
| Dependência de legado | Sistema antigo sem dono ativo | Expectativas automatizadas impossíveis, ACL protege o downstream |
O problema que resolve
A integração sem padrão definido produz três falhas recorrentes em produção. A primeira é a expectativa implícita: o time consumidor monta sua lógica sobre o comportamento observado da API do fornecedor, sem que o fornecedor saiba quais partes são críticas, e uma mudança considerada trivial derruba o consumidor. A segunda é a quebra de prioridade: quando o fornecedor reorganiza o roadmap, as demandas do consumidor não têm representação formal e ficam sempre para depois. A terceira é o diagnóstico cruzado: em incidente, o consumidor descobre a quebra junto com o usuário, porque não existe mecanismo que teste o contrato antes do deploy.
O Customer-Supplier ataca as três falhas com o mesmo mecanismo. A expectativa explícita e automatizada transforma comportamento crítico em teste que roda no CI do fornecedor. O canal de negociação dá ao consumidor assento no planejamento do fornecedor. O contrato versionado dá ao diagnóstico um ponto de comparação, porque a pergunta muda de o que aconteceu para qual diferença entre versões explica o comportamento.
Automação das expectativas
O coração operacional do padrão é colocar os testes de expectativa do downstream dentro do pipeline do upstream. Contratos guiados por consumidor, implementados em ferramentas como Pact, materializam essa ideia: o consumidor publica o pacto com as interações de que precisa, e o fornecedor verifica o pacto contra a implementação atual antes de qualquer merge.
# Fluxo de contrato guiado por consumidor entre checkout e precificação
fluxo_pact:
atores:
upstream: "servico-precificacao"
downstream: "servico-checkout"
etapas:
- passo: "checkout executa testes contra mock e publica o pacto"
conteudo: "GET /precos com sku e quantidade, espera 200 e total"
- passo: "broker de pactos registra a versão do pacto e do consumidor"
- passo: "pipeline da precificação busca pactos pendentes e verifica"
- passo: "verificação vermelha bloqueia o merge no upstream"
- passo: "checkout consulta can-i-deploy antes do próprio deploy"
regra:
compatibilidade: "nenhuma mudança de schema que viole pacto entra em produção"
O mecanismo muda o regime de falha. Sem verificação, a quebra aparece em produção e vira incidente com dois times mobilizados. Com verificação, a quebra aparece no CI do fornecedor, minutos depois do push, com o pacto violado apontando exatamente qual interação deixou de ser atendida. O custo da falha despenca de incidente para ajuste de código.
{
"consumer": { "name": "checkout", "version": "2.4.1" },
"provider": { "name": "precificacao" },
"interactions": [
{
"description": "consulta preço total com desconto por volume",
"request": {
"method": "GET",
"path": "/precos",
"query": { "sku": "ABC-10", "quantidade": "150" }
},
"response": {
"status": 200,
"body": {
"sku": "ABC-10",
"preco_unitario": "39.90",
"desconto_aplicado": "0.05",
"total": "5686.50"
},
"matchingRules": {
"$.body.total": { "match": "type" },
"$.body.desconto_aplicado": { "match": "regex": "^0\\.[0-9]{2}$" }
}
}
}
]
}
O detalhe técnico que evita contratos frágeis está nas regras de correspondência. Um pacto que fixa valores literais quebra em qualquer mudança de preço, e a equipe passa a ignorar falhas de contrato, o que anula o mecanismo. As regras de tipo e de formato, como qualquer número para total e regex para o formato do desconto, mantêm o pacto focado no que importa: a existência dos campos, os tipos e o formato, e não o dado de exemplo.
Em ecossistemas sem ferramenta dedicada, a mesma ideia funciona com testes de contrato escritos à mão: o upstream mantém uma suíte chamada expectativas-checkout, acordada com o consumidor, e a suíte roda em todo pipeline. O formato importa menos do que o princípio, que é o comportamento do upstream ser verificado contra a necessidade declarada do downstream, e não contra a opinião de quem escreveu a mudança.
Contrato e compatibilidade
O contrato é a materialização escrita do acordo, e a política de compatibilidade é a parte que evita incidentes. A política define quais mudanças são seguras, quais exigem transição e quais são proibidas sem nova versão.
| Mudança no contrato upstream | Classificação | Procedimento exigido |
|---|---|---|
| Adicionar campo opcional na resposta | Compatível | Nenhum, consumidor ignora o que não conhece |
| Adicionar campo obrigatório na requisição | Incompatível | Versão nova ou aceitar default no servidor |
| Renomear campo | Incompatível | Período com os dois nomes e deprecação do antigo |
| Remover campo da resposta | Incompatível | Janela acordada com confirmação dos consumidores |
| Tornar campo opcional em obrigatório no retorno | Compatível com cautela | Avisar consumidores que já tratam ausência |
| Alterar códigos de erro existentes | Incompatível | Nova versão, código antigo mantido na transição |
| Adicionar endpoint novo | Compatível | Publicação na documentação e changelog |
A compatibilidade retroativa é a regra padrão, e as exceções precisam de processo. Mudança incompatível exige versão nova do endpoint ou do schema, janela de sobreposição em que as duas versões respondem, comunicação formal aos consumidores e métrica de consumo da versão antiga até chegar a zero. O erro clássico é manter a política apenas no boca a boca; a política precisa estar no README do contrato, com exemplos de cada classificação, para sobreviver à rotatividade das equipes.
No canal assíncrono, o mesmo acordo assume forma de schema de evento com registro de schemas centralizado. O upstream publica a versão do schema, o consumidor valida compatibilidade antes de migrar o consumidor do tópico, e o teste de expectativa verifica que o evento contém os campos de que o downstream depende. O erro específico desse canal é o consumidor ler o evento cru sem camada de desserialização versionada, o que espalha suposições de formato pelo código e impede a migração controlada de versões.
Coordenação de roadmap entre times
O componente social do padrão é tão determinante quanto o técnico, e a coordenação de roadmap é o ponto em que ele aparece. O downstream não é apenas consumidor de API; é cliente do upstream, e clientes negociam prazo, escopo e prioridade. A prática que sustenta a negociação é a revisão periódica registrada, na qual o downstream apresenta demandas com impacto de negócio e o upstream as avalia contra a capacidade e os outros consumidores.
Uma agenda típica de revisão cobre os itens a seguir:
- Demandas novas do downstream, com caso de negócio e prazo desejado, avaliadas contra o backlog do upstream.
- Pendências em andamento, com status real, bloqueios e responsável, evitando o relatório de otimismo que esconde atraso.
- Mudanças planejadas pelo upstream que afetam consumidores, comunicadas com antecedência suficiente para adaptação.
- Métricas do contrato, como volume de chamadas, latência nos percentis altos e taxa de erro vista pelo consumidor.
- Revisão dos pactos de expectativa, removendo interações que deixaram de existir e adicionando as que surgiram.
Quando o upstream serve muitos consumidores, a negociação vira arbitragem, e a solução usual é a extensão controlada: a plataforma oferece pontos de extensão, como campos customizáveis e webhooks, e cada consumidor adapta dentro do que a plataforma fornece. O Customer-Supplier continua válido, com a diferença de que a influência do downstream se exerce sobre a evolução das extensões e não sobre o núcleo do serviço.
Relação com sistemas externos e legados
O padrão pressupõe os dois lados sob coordenação dentro da mesma organização, e é nisso que ele difere de alternativas voltadas a fronteiras externas. Provedor de pagamento, plataforma de mensageria e qualquer SaaS contratado não participam do seu processo de priorização, não rodam seus pactos e não negociam roadmap com você. Nesses casos, a relação real é de dependência unilateral, e a estratégia correta migra para Conformist, quando o modelo externo é aceito diretamente, ou para ACL, quando o modelo externo precisa ser traduzido.
Legado interno cria uma variante interessante: o sistema antigo é upstream de fato, mas não tem equipe capaz de rodar testes de expectativa nem de assumir prioridades. A saída usual é o downstream assumir sozinho o papel de protetor, com ACL no lado consumidor e instrumentação intensa de compatibilidade, enquanto se planeja a substituição do legado. Reconhecer essa limitação com honestidade é melhor do que fingir um acordo que não existe, porque o falso acordo esconde risco que ninguém gerencia.
Aplicação prática: exemplo de extremo a extremo
Um caso concreto amarra os componentes. O serviço de precificação é o upstream, e o checkout é o downstream. O checkout precisa do preço total com desconto por volume, e essa interação vira pacto publicado no broker. O pipeline da precificação verifica o pacto em todo pull request, e uma proposta de mudança no formato de desconto é bloqueada porque viola a expectativa registrada. O time do checkout abre a demanda na revisão mensal, argumentando com o impacto na conversão, e os times acordam um novo campo de desconto detalhado em versão dois do endpoint. Durante seis semanas as duas versões respondem, com métrica de consumo da versão um acompanhada semanalmente. Quando o consumo da versão antiga chega a zero, ela entra em deprecação formal e é removida no ciclo seguinte. Nenhuma etapa desse fluxo depende de sorte ou boa vontade; cada uma tem artefato, dono e mecanismo de verificação.
Erros comuns e boas práticas
| Sintoma em produção | Causa raiz | Mitigação |
|---|---|---|
| Consumidor quebrado por mudança trivial do upstream | Expectativa implícita, sem pacto automatizado | Contratos guiados por consumidor no pipeline |
| Demandas do consumidor sempre adiadas | Sem canal formal de negociação | Revisão periódica registrada com priorização |
| Incidente descoberto pelo usuário final | Nenhum teste de contrato antes do deploy | Verificação de pacto bloqueando merge |
| Versões antigas vivendo indefinidamente | Deprecação sem prazo e sem métrica de consumo | Janela acordada e dashboard de consumo por versão |
| Acordo perdido com a troca de pessoas | Relação baseada em contato informal | Documentação do acordo e do canal de decisão |
A boa prática que separa implementações maduras de implementações nominais é a exigência de can-i-deploy no deploy do consumidor. Sem essa verificação, o consumidor pode publicar dependência de recurso que ainda não existe no upstream, e a integração quebra no ambiente de staging com dois times procurando culpa. Com a verificação, o deploy do consumidor é bloqueado até que a versão do upstream com o recurso verificado esteja em produção. A segunda prática decisiva é tratar o pacto como código revisado: mudança de expectativa passa por pull request no repositório do consumidor, com revisão do time do upstream quando a mudança é estrutural. Pacto que muda sem revisão é contrato sem validade, e a confiança acumulada entre os times evapora na primeira quebra não detectada.