Pular para o conteudo principal

Contratos internos de comando e consulta sem payload genérico demais

Quando tudo entre módulos vira um payload genérico com campos soltos, o backend perde intenção, validação e clareza de responsabilidade.

Andrews Ribeiro

Andrews Ribeiro

Founder & Engineer

O problema

Quando dois módulos internos precisam conversar, muita equipe cai num atalho:

  • cria um payload genérico
  • reaproveita um DTO enorme
  • deixa metade dos campos opcionais

No começo isso parece prático.

Depois começa a bagunça:

  • ninguém sabe o que é obrigatório
  • query recebe campos que só faziam sentido em command
  • comando recebe estrutura pensada para tela
  • um módulo muda e quebra outro por acidente

O sistema continua compilando.

Mas para de ficar claro.

Modelo mental

Vale separar duas intenções bem diferentes:

  • command: “faça isto”
  • query: “me mostre isto”

Command precisa de dados mínimos para decidir e executar.

Query precisa de critério de busca e formato de resposta adequado ao consumo.

Quando os dois usam o mesmo pacote genérico, a intenção se embaralha.

Exemplo simples

Imagine um módulo de faturamento conversando com o de pedidos.

Um command pode ser algo como:

  • GenerateInvoiceForOrder(orderId, billingProfileId)

Uma query pode ser algo como:

  • GetInvoiceSummary(orderId)

Se os dois começarem a trafegar um objeto enorme OrderPayload, com:

  • endereço
  • itens
  • total
  • flags
  • metadados
  • campos opcionais

logo ninguém sabe qual parte é contrato e qual parte é carga acidental.

O erro comum

O erro comum é chamar isso de “flexibilidade futura”.

Na prática, muitas vezes é só fronteira frouxa.

O payload genérico demais cria alguns efeitos previsíveis:

  • validação fraca
  • campos usados por acidente
  • acoplamento escondido
  • dependência difícil de rastrear

Quanto mais o contrato aceita qualquer coisa, menos ele protege o sistema.

Onde a decisão costuma ficar melhor

Normalmente, o contrato interno deveria nascer perto do caso de uso que realmente precisa daquela conversa.

Isso ajuda a preservar:

  • a intenção do comando
  • a forma da consulta
  • a responsabilidade de cada lado

Não é sobre criar classe para tudo.

É sobre não tratar qualquer troca interna como se fosse saco de dados sem dono.

Como um senior pensa

Quem tem mais julgamento costuma perguntar:

  • este módulo realmente precisa saber tudo isso?
  • estou mandando dado porque é necessário ou porque já veio no pacote?
  • esse contrato representa uma intenção ou só um dump de estrutura interna?
  • command e query estão claros ou misturados?

Essas perguntas deixam a arquitetura menos teatral e mais estável.

Ângulo de entrevista

Esse tema aparece em backend, modularização e design de código.

O entrevistador quer ver se você entende:

  • que contrato interno também é arquitetura
  • que command e query têm semânticas diferentes
  • que payload genérico demais aumenta acoplamento invisível

Resposta forte costuma soar assim:

“Eu tentaria manter contratos internos orientados à intenção. Command e query não precisam trafegar o mesmo DTO genérico. Quanto mais o contrato diz claramente o que o módulo precisa e devolve, menor o acoplamento acidental depois.”

Takeaway direto

Contrato interno bom não transporta tudo o que existe.

Transporta só o que a conversa realmente quer dizer.

Resumo rápido

O que vale manter na cabeça

Checklist de pratica

Use isto ao responder

Você concluiu este artigo

Próximo artigo Controle de admissão no backend: quando rejeitar cedo é melhor do que falhar tarde Artigo anterior Contrato interno entre módulos sem inventar RPC dentro do mesmo app

Continue explorando

Artigos relacionados