← voltar para devlogs

Arquitetura de software: por que não existe bala de prata

15 de out. de 2026, 17:30·5 min de leituraarquitetura de softwaredddclean architecturescreaming architecture

Toda vez que alguém pergunta "qual é a arquitetura certa pra esse projeto?" a resposta honesta é "depende". Não existe bala de prata em arquitetura de software — o que funciona bem pra um time de cinquenta pessoas pode ser puro over-engineering pra um MVP de duas semanas, e vice-versa.

O que realmente define a escolha

  • Tamanho do time
  • Tempo de vida esperado do projeto
  • Complexidade do domínio de negócio
  • Recursos disponíveis (tempo, orçamento, experiência do time)

Esses fatores puxam em direções diferentes. Os três exemplos abaixo mostram abordagens distintas, cada uma com um trade-off diferente entre rigor e velocidade.

Exemplo 1 — DDD (Domain-Driven Design)

Leia mais em Domain-Driven Design, de Martin Fowler.

DDD não é uma estrutura de pastas, é uma forma de pensar o domínio de negócio antes de pensar em código.

  • Linguagem ubíqua — o time (negócio + dev) usa os mesmos termos pra descrever o domínio, sem tradução entre "como o cliente chama" e "como o código chama".
  • Bounded context — fronteiras explícitas entre partes do sistema que têm seu próprio modelo e vocabulário, mesmo que falem sobre a "mesma" entidade de formas diferentes.
  • Agregados, entidades e value objects — formas de modelar regras de negócio garantindo consistência dentro de uma fronteira transacional.

Quando ajuda: domínios genuinamente complexos, com regras de negócio que não mapeiam bem pra CRUD (ex: sistemas financeiros, logística, seguros).

Quando é fricção: a maioria dos projetos não chega perto desse nível de complexidade. Aplicar todo o aparato estratégico do DDD (bounded contexts, agregados, linguagem ubíqua documentada) em um CRUD simples é pagar um preço de modelagem que o domínio não pede.

Exemplo 2 — Clean Architecture

Leia mais em The Clean Architecture, de Robert C. Martin (Uncle Bob).

Organiza o código em camadas concêntricas, com uma regra única: dependências sempre apontam pra dentro.

  • Entities — regras de negócio mais centrais, independentes de tudo.
  • Use Cases — orquestram as regras de negócio pra um caso de uso específico.
  • Interface Adapters — controllers, presenters, gateways que traduzem entre o mundo externo e os use cases.
  • Frameworks & Drivers — banco de dados, web framework, UI — tudo que é detalhe de implementação.

Quando ajuda: quando testabilidade e independência de framework importam de verdade — por exemplo, trocar banco de dados ou subir a mesma regra de negócio em contextos diferentes (API, CLI, worker).

Quando é fricção: o número de camadas e interfaces de indireção cresce rápido. Em um projeto pequeno, isso costuma significar mais arquivos e mais saltos pra entender um fluxo simples, sem ganho real — ninguém vai trocar de banco de dados num MVP.

Exemplo 3 — Package by Feature / Screaming Architecture

Leia mais em Screaming Architecture, também de Robert C. Martin.

Essa é a abordagem que eu sigo geralmente nos meus projetos. Em vez de organizar o projeto por camada técnica (uma pasta controllers/ com tudo, uma services/ com tudo, uma repositories/ com tudo — o chamado "package by layer"), organizo por domínio de negócio ("package by feature"). Cada domínio vira uma pasta praticamente autocontida:

/domains
  /funcionario
    funcionario.controller.ts
    funcionario.service.ts
    funcionario.repository.ts
    funcionario.schema.ts
  /departamento
    departamento.controller.ts
    departamento.service.ts
    departamento.repository.ts
    departamento.schema.ts

A ideia do Uncle Bob em "Screaming Architecture" é que a estrutura de pastas deveria "gritar" o domínio do negócio, não o framework — olhando a árvore de pastas, dá pra saber que o sistema lida com funcionários e departamentos, não que foi feito em Express ou NestJS. É uma aplicação mais pragmática da ideia de "separar por domínio" do DDD, sem todo o aparato estratégico (não preciso de bounded context documentado ou agregado formal pra saber que "funcionário" e "departamento" são conceitos diferentes).

Vantagens que eu sinto no dia a dia:

  • Tudo que é relacionado a uma feature está num lugar só — não preciso pular entre quatro pastas técnicas pra entender o fluxo completo de "funcionário".
  • Escala bem com o time: dá pra ter um time (ou uma pessoa) dono de um domínio sem pisar na pasta de outro.
  • Remover ou isolar um domínio inteiro é simples — é só a pasta.
  • Reduz conflito de merge, porque mudanças tendem a ficar concentradas dentro de um domínio.

Trade-off: regra de negócio que cruza domains é o ponto de atenção. Se funcionario depende de departamento, a tentação é importar o departamento.repository direto dentro do funcionario.service, criando acoplamento forte entre domains. O jeito mais saudável é funcionario.service conversar com departamento.service (ou com uma interface), nunca pular direto pro repository do outro domínio — senão a vantagem de isolamento se perde.

O que eu acho disso

Nenhuma dessas três é "a certa". São ferramentas com preços diferentes.

DDD completo só se paga quando o domínio de negócio é genuinamente complexo — a maioria dos projetos que eu vi não chega nem perto disso, e aplicar todo o ritual (linguagem ubíqua documentada, bounded contexts, agregados) em cima de um domínio simples é o tipo clássico de over-engineering: você gasta mais tempo modelando do que entregando.

Clean Architecture ajuda quando testabilidade e independência de framework importam de verdade. Mas o preço é indireção — camadas, interfaces, mais arquivos pra seguir um fluxo simples. Em projeto pequeno isso vira atrito sem retorno.

Package by Feature / Screaming Architecture é a que eu mais uso no dia a dia justamente porque entrega grande parte do benefício do DDD (coesão, fronteiras claras) com uma fração do custo de modelagem. Mas também tem limite: se o domínio de negócio for de fato complexo, só separar pastas não resolve — em algum momento as regras de consistência e os agregados do DDD fazem falta.

No fim, arquitetura é ferramenta pra resolver problemas concretos — comunicação de time, manutenibilidade, velocidade de entrega. Escolher a "mais bonita" ou a "mais na moda" sem olhar pro contexto do projeto é o erro mais comum que eu vejo. A pergunta certa nunca é "qual arquitetura é a melhor", e sim "qual o problema que eu tenho agora, e qual arquitetura resolve esse problema com o menor custo".