Como estruturar um projeto back-end limpo e organizado?
Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!
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.