Pular para o conteudo principal

APIs e serviços com fronteiras claras

Como desenhar fronteiras entre rotas, serviços e responsabilidades sem transformar o sistema num monte de acoplamento escondido.

Andrews Ribeiro

Andrews Ribeiro

Founder & Engineer

Trilha

Trilha de system design para entrevistas

Etapa 4 / 19

O problema

Muita API fica confusa não porque faltou framework, mas porque faltou combinado sobre responsabilidade.

Ai aparece o tipo de código que todo mundo reconhece:

  • controller valida formato, faz regra, consulta banco e devolve resposta
  • service conhece req, res, status code e cabecalho
  • repository decide desconto, status inicial e regra de negócio
  • helper solto faz metade do fluxo e ninguém sabe se pode mexer

Enquanto o fluxo esta pequeno, isso parece só “jeito do projeto”. O problema aparece quando a regra muda, quando entra outro canal alem do HTTP ou quando o time precisa depurar um incidente. Nessa hora, a pergunta vira: onde essa decisão deveria morar?

Modelo mental

Fronteira boa e a que reduz dúvida.

Quando alguém abre o código, deveria ficar fácil responder:

  • quem recebe input externo
  • quem valida formato, permissao e contrato
  • quem toma a decisão de negócio
  • quem fala com banco, fila ou provider
  • quem traduz o resultado para o formato da API

Você não precisa de arquitetura cerimonial para isso. Precisa separar quatro tipos de responsabilidade que costumam se misturar:

  1. Transporte: HTTP, fila, RPC, status code, serialização.
  2. Regra de negócio: decisão sobre o que pode ou não pode acontecer.
  3. Infraestrutura: banco, cache, provider externo, fila.
  4. Apresentacao do resultado: formato da resposta, mensagem, payload.

Uma regra prática ajuda muito:

se o mesmo arquivo decide regra de negócio e ao mesmo tempo conhece detalhe de HTTP ou persistencia, a fronteira já começou a borrar.

Quebrando o problema

Uma divisao simples, que funciona em bastante sistema real, costuma ser esta:

  1. A rota recebe o request, valida o básico, autentica e chama um fluxo.
  2. O service ou use case concentra a regra e orquestra o caminho feliz e o caminho de erro.
  3. Repository, client ou gateway falam com banco, fila e serviço externo.
  4. A camada de entrada traduz o resultado para a resposta da API.

Isso não e religiao. E só uma forma de impedir um arquivo central que faz tudo.

Outra pergunta boa e esta:

se a regra de negócio mudar amanha, onde eu espero mexer primeiro?

Se a resposta honesta for “talvez em tres controllers, dois helpers e um repository”, a fronteira esta ruim.

Heuristicas que funcionam bem

Tem alguns sinais bem práticos de fronteira borrada:

  • se para testar a regra de negócio você precisa mockar req ou res, a regra esta presa no lugar errado
  • se o service devolve status: 409 ou mensagem HTTP, ele esta sabendo demais sobre transporte
  • se o repository decide “pedido nasce como pending ou approved”, ele esta carregando regra demais
  • se a rota conhece SQL, timeout de provider e fallback de cache, ela virou centro de comando

O oposto também e verdade. Fronteira melhor costuma produzir um efeito bom:

  • da para mudar a regra sem reabrir a camada HTTP inteira
  • da para trocar provider sem reescrever decisão de negócio
  • da para reusar o mesmo fluxo em API, worker e job interno

Exemplo simples

Imagina um endpoint de criação de pedido.

Numa versão confusa, o controller faz tudo:

  • valida carrinho
  • checa estoque
  • calcula frete
  • define status inicial
  • salva no banco
  • manda email

Funciona até o dia em que:

  • o frete muda
  • a confirmação passa a sair por evento
  • o mesmo fluxo precisa rodar num worker de reprocessamento

Agora pensa na mesma operação com fronteira melhor:

  • a rota valida input, autenticação e contrato
  • o service createOrder decide estoque, total, frete, status inicial e efeitos necessários
  • o repository persiste pedido e itens
  • um client fala com provider de pagamento
  • um evento ou fila dispara notificação depois da confirmação

O ganho não e estetico. E operacional.

Se a regra de frete mudar, você já sabe onde ir. Se trocar banco, a regra principal não precisa aprender SQL novo. Se email falhar, o controller não precisa virar um bloco de try/catch que sabe tudo sobre o fluxo.

Onde muita gente erra

  • espalhar regra de negócio entre várias camadas sem critério
  • criar camada demais sem responsabilidade distinta
  • acoplar o formato da API ao formato interno da tabela
  • fazer controller virar service com nome diferente
  • discutir arquitetura como estrutura de pasta em vez de responsabilidade

Também existe o erro contrario: exagerar na separação.

Tem endpoint simples que não precisa de cinco objetos, tres interfaces e dois factories. Se a operação e trivial e a chance de reuso e baixa, simplificar pode ser a melhor escolha. O ponto não e multiplicar arquivos. E evitar mistura perigosa.

Como um senior pensa

Quem tem mais experiência costuma olhar primeiro para manutenção e mudança, não para pureza arquitetural.

O raciocínio geralmente e este:

“Eu quero que a regra importante fique num lugar previsivel. E quero que detalhes de HTTP, banco e provider não vazem para onde a decisão mora.”

Isso ajuda em tres frentes ao mesmo tempo:

  • diminui efeito cascata quando a regra muda
  • facilita teste no nivel certo
  • deixa mais fácil reaproveitar o mesmo fluxo fora da API

Em trabalho real, isso vale mais do que o diagrama bonito. Em entrevista, mostra que você não esta recitando padrão. Esta tentando diminuir custo de manutenção.

O que o entrevistador quer ver

Quando o assunto e fronteira entre API e serviço, o entrevistador normalmente quer perceber se você:

  • separa responsabilidade com critério
  • sabe onde a regra de negócio deve morar
  • evita acoplamento entre HTTP, regra e persistencia
  • pensa em mudança futura, não só em primeira entrega

Uma resposta forte costuma soar assim:

“Controller recebe e valida o request. A regra principal fica no fluxo de negócio. Banco e provider ficam atras de uma camada que eu consigo trocar sem mover a decisão principal.”

Isso passa uma mensagem boa: você pensa em fronteira para diminuir risco, não para parecer sofisticado.

API boa não e a que tem mais camada. E a que deixa obvio onde cada decisão mora.

Resumo rápido

O que vale manter na cabeça

Checklist de pratica

Use isto ao responder

Você concluiu este artigo

Parte da trilha: Trilha de system design para entrevistas (4/19)

Próximo artigo RAG vs Fine-Tuning Sem Falso Dilema Artigo anterior Escala e gargalos

Continue explorando

Artigos relacionados