Fronteiras de backend não são diagramas. São acordos sobre quem é dono de quê, como as coisas mudam, como falham e quem responde por elas, e um acordo só funciona se o time inteiro consegue enxergá-lo. Uma fronteira só serve se continuar orientando decisões quando o time está sob pressão de entrega.
O problema
A maioria dos backends começa com fronteiras que parecem limpas e que ficam ambíguas com o tempo. Chegam integrações com sistemas de terceiros. Aparecem funcionalidades novas que precisam ser construídas do zero. E quase nunca alguém tem clareza de como o software vai estar daqui a cinco anos.
Cada uma dessas coisas empurra as fronteiras um pouco, e sem uma base comum para o design, cada empurrão é resolvido de um jeito diferente.
Onde os times costumam errar
É comum desenhar as fronteiras antes de conhecer os requisitos não funcionais. O time divide serviços por substantivo, por repositório ou pela conveniência do deploy antes de decidir quem é dono do comportamento, quem paga o custo da mudança e que carga e disponibilidade o sistema realmente precisa suportar.
O erro oposto é projetar para um cenário de cinco anos que ninguém consegue enxergar ainda: montar toda a estrutura para 100.000 usuários quando o sistema tem 10.000 hoje.
Princípios
- Requisitos não funcionais vêm antes das fronteiras.
- Mantenha o design o mais simples possível. Você não vai precisar disso (YAGNI).
- Ownership é a primeira fronteira.
- Contratos importam mais do que pastas.
- Dado compartilhado é sinal de alerta de design até prova em contrário.
- O formato da integração precisa refletir como ela pode falhar.
- Uma fronteira que não dá para testar ainda não existe de verdade.
- Os acordos ficam visíveis para o time inteiro.
- Deixe o código melhor do que você encontrou.
Modelo de decisão
Eu começo pelos requisitos não funcionais, porque são os fundamentos de engenharia sobre os quais todas as outras decisões se apoiam:
- Quantos usuários o sistema vai suportar?
- Quantos clientes vão usar o sistema?
- Ele precisa funcionar 24/7?
- Quais provedores de nuvem vamos usar?
Depois eu esboço o sistema com o modelo C4: o contexto em que o sistema vive, os containers que o compõem e os componentes dentro deles. É o jeito mais rápido que eu conheço de ver como as peças interagem, por onde entram as integrações de terceiros e onde uma fronteira realmente faz diferença.
Com as duas coisas em mãos, eu posiciono as fronteiras usando como eixos de decisão o ownership, a volatilidade, a autoridade sobre os dados, a latência, o isolamento de falhas e a independência de deploy.
Padrões práticos
Projete para os requisitos que você tem, não para os que talvez venha a ter. Se o sistema atende 10.000 usuários hoje e uma projeção de cinco anos fala em 100.000, não construa agora a estrutura para 100.000. Isso é YAGNI: você não vai precisar disso, e se precisar, vai saber muito mais sobre o problema quando chegar a hora.
Comece com poucas fronteiras, deixe os contratos explícitos e só extraia um serviço quando o ownership ou a volatilidade justificarem o custo operacional.
Deixe os acordos visíveis. Se as fronteiras, os contratos e os donos vivem na cabeça de uma pessoa só, o design começa a desandar na primeira vez que essa pessoa estiver ocupada.
Siga a regra do escoteiro. Num sistema ou ecossistema legado, cada mudança deve deixar a área um pouco melhor do que estava, sem quebrar nada em produção. Quando você está construindo um serviço ou um backend novo do zero, defenda essa mesma regra desde o primeiro commit, para que ela vire hábito do time antes de existir legado para arrumar.
Tradeoffs
Fronteiras largas reduzem o custo operacional, mas podem esconder acoplamento de domínio. Fronteiras estreitas dão mais autonomia, mas aumentam o custo de coordenação.
Projetar para os números de hoje significa que algumas partes vão precisar de retrabalho se o crescimento vier. Projetar para a projeção de cinco anos significa pagar agora pela complexidade de atender usuários que talvez nunca apareçam, e esse segundo custo costuma doer mais.
As melhorias da regra do escoteiro aumentam um pouco o escopo de cada mudança. Se forem pequenas e seguras, elas se acumulam sem colocar a produção em risco.
Fluxo de exemplo
- Escreva os requisitos não funcionais: usuários, clientes, disponibilidade e provedores de nuvem.
- Esboce as visões de contexto e de containers do C4 para ver como os componentes e as integrações de terceiros interagem.
- Mapeie a capacidade de negócio, dê nome ao dono e identifique a fonte da verdade.
- Defina o contrato com o consumidor, incluindo os contratos com terceiros, e escreva testes de contrato.
- Escolha o formato de deploy mais simples que atende aos requisitos de hoje.
- Registre os acordos num lugar onde o time inteiro consiga ver.
- Em cada mudança seguinte, deixe a área um pouco melhor sem quebrar a produção.
Checklist
- Os requisitos não funcionais estão escritos.
- Existe uma visão de contexto e de containers do C4.
- O design está dimensionado para os requisitos atuais, não para uma projeção.
- Está claro quem é dono do comportamento.
- Está clara a fonte da verdade.
- Os consumidores usam o contrato sem precisar conhecer o interior do serviço.
- O comportamento em caso de falha está documentado.
- Os testes protegem o contrato público.
- Os acordos estão visíveis para o time inteiro.
Guias relacionados
- Engenharia backend com IA sem perder o rigor arquitetural
- Como eu tomo decisões de arquitetura sem fingir certeza
Leve isto para o seu time
Este tema funciona bem como workshop para times que estão quebrando um monólito, integrando sistemas de terceiros, começando um backend novo do zero ou organizando o ownership de um backend legado.