24 de Julho de 2025
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
Founder & Engineer
3 min Intermediario Sistemas
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
- Contrato interno bom preserva intenção do fluxo, não só transporte de dados.
- Command e query pedem formatos diferentes porque fazem perguntas diferentes ao sistema.
- Payload genérico demais dá sensação de flexibilidade e cobra a conta em ambiguidade depois.
- Campo opcional demais costuma esconder fronteira mal definida entre módulos.
Checklist de pratica
Use isto ao responder
- Consigo dizer quais dados esse comando realmente precisa para acontecer?
- Minha query devolve só o que o consumidor precisa ou um pacote genérico por conveniência?
- Estou reaproveitando DTO porque faz sentido ou porque ninguém quis definir fronteira?
- Se um campo mudar, sei quem realmente depende dele?
Você concluiu este artigo
Compartilhar esta página
Copie o link manualmente no campo abaixo.