Como estruturar um projeto back-end limpo e organizado?
Como estruturar um projeto back-end limpo e organizado?
Oii, tudo bem?
Perfeito, Leide. Vou falar um pouco sobre.
Um back-end limpo nasce de separar o “que” do “como”. Pense em quatro blocos: a entrada (HTTP/GraphQL/CLI), a aplicação (casos de uso), o domínio (regras de negócio puras) e a infra (banco, cache, e-mails, integrações). A entrada recebe a requisição, valida e traduz para um caso de uso. A aplicação orquestra a tarefa. O domínio decide o que é permitido. A infra só executa efeitos colaterais. Assim, trocar um banco ou um provedor de e-mail não muda sua regra de negócio.
Uma estrutura simples ajuda muito:
/src
/api # controladores, validações e mapeamento de erro->HTTP
/app # casos de uso: cadastrarCliente, fecharPedido...
/domain # entidades, value objects e serviços de domínio
/infra # orm/repos, http clients, filas, email
/config # env, logger, container de DI
/tests # unitários (domínio) e integração (infra/api)
No dia a dia, o fluxo fica claro. A requisição chega ao controlador, que valida dados e chama um caso de uso. Esse caso de uso conversa com interfaces (ex.: ClienteRepo
, EnviarEmail
) sem saber qual ORM ou serviço externo você usa. As implementações reais dessas interfaces moram na camada de infra. Nos testes, você injeta dublês (mocks/fakes) para isolar cenários. Esse desacoplamento torna o código previsível e fácil de evoluir.
Erros e logs também seguem um caminho único. Em vez de espalhar try/catch
, defina erros de domínio (ex.: EstoqueInsuficiente
) e deixe um tratador central na API convertê-los em respostas HTTP consistentes. Faça logs estruturados com um requestId
em cada linha; isso simplifica rastrear um problema do começo ao fim. Para acompanhar a saúde, exponha health checks (/health
e /ready
) e colete métricas de latência, throughput e taxa de erro.
Configuração deve vir de variáveis de ambiente. Nada de segredos no repositório. Tenha padrões seguros para produção (timeouts, retry, rate limit, CORS e headers de segurança) e valores amigáveis para desenvolvimento (ex.: banco local via Docker). Migrações de banco são versionadas e aplicadas automaticamente no deploy; transações ficam próximas ao caso de uso que precisa delas.
Sobre qualidade, foque onde dói. Teste unitário cobre o domínio (barato e rápido). Teste de integração garante que repositórios, filas e clientes HTTP conversam de verdade. Se você integra com outros times, contratos versionados (OpenAPI/Pact) evitam surpresas. Mire cobertura suficiente para proteger regras críticas, não um número mágico.
O ciclo de vida precisa ser previsível. Um README
explica como rodar, testar e depurar. Scripts simples (make
ou npm run
) padronizam tarefas comuns. Na CI, rode lint, testes e varredura de vulnerabilidade. Na CD, use pipelines idempotentes e configuração por ambiente. Quando tomar decisões grandes (trocar ORM, adotar filas), anote um ADR curto: contexto, decisão e por quê.
Por fim, trate sua API como um produto. Versione (/v1
), documente (OpenAPI/Swagger), seja consistente em códigos de status e idempotente em operações sensíveis (retries sem duplicar efeitos). Quando for deprecá-la, comunique com tempo e mantenha comportamento estável.
É um assunto extenso mas com tempo e prática fica mais fácil de entender.