Se você já trabalha com desenvolvimento de software — ou está começando agora — provavelmente já sentiu aquela sensação de que o projeto começou simples, mas aos poucos virou um emaranhado de arquivos, dependências e regras de negócio espalhadas por todo lado. É exatamente nesse cenário que entra a Clean Architecture, um dos design patterns mais importantes para quem quer criar sistemas mais organizados, testáveis e fáceis de evoluir ao longo do tempo.
A Clean Architecture não é um framework e nem algo exclusivo de uma linguagem específica. Ela é uma forma de pensar a estrutura da sua aplicação, separando responsabilidades e deixando bem claro o papel de cada parte do sistema. O objetivo principal é simples: proteger o coração da aplicação — a regra de negócio — de mudanças externas, como frameworks, banco de dados ou interfaces.
Neste artigo, vamos falar sobre Design Patterns e Clean Architecture de forma prática, com uma linguagem bem direta, como se estivéssemos conversando. A ideia é que você entenda não só o o que é, mas principalmente o por que usar e como aplicar no dia a dia. Vamos usar exemplos com NestJS, já que ele se encaixa muito bem nesse tipo de arquitetura e é bastante popular no ecossistema Node.js.
Se você é iniciante, fique tranquilo: não é preciso ter domínio avançado de arquitetura para acompanhar. Vamos construir o raciocínio passo a passo, mostrando como a Clean Architecture ajuda a evitar código acoplado, facilita testes e torna o projeto mais profissional — algo muito valorizado em entrevistas e no mercado de trabalho.
Ao longo do conteúdo, você vai entender conceitos como camadas, dependências, entidades, casos de uso e como tudo isso se conecta em um projeto real. No final, você vai perceber que Clean Architecture não é algo “complexo demais”, mas sim uma ferramenta poderosa quando usada da forma certa.
O que é Clean Architecture?
A Clean Architecture é um modelo de arquitetura de software criado para resolver um problema muito comum no desenvolvimento: o crescimento desorganizado do código ao longo do tempo. Sabe quando uma aplicação começa pequena, mas depois de alguns meses fica difícil de entender, testar ou mudar qualquer coisa sem quebrar outra? A Clean Architecture nasce exatamente para evitar isso.
Na prática, ela propõe uma separação clara de responsabilidades, organizando o sistema em camadas bem definidas. Cada camada tem um papel específico e regras claras de dependência. O ponto mais importante aqui é: as regras de negócio não devem depender de frameworks, bancos de dados ou detalhes externos.
Isso significa que sua lógica principal continua a mesma mesmo que você troque o banco de dados, a API REST por GraphQL ou até o próprio framework.
O princípio central da Clean Architecture
O coração da Clean Architecture é a chamada regra da dependência. Ela diz o seguinte:
as dependências sempre apontam para dentro, nunca para fora.
Em outras palavras, as camadas externas podem depender das internas, mas o contrário nunca deve acontecer.
Normalmente, temos algo assim:
- Entidades: regras de negócio mais puras
- Casos de uso (Use Cases): regras de aplicação
- Interface Adapters: controllers, presenters, gateways
- Frameworks & Drivers: NestJS, banco de dados, HTTP, etc.
Quanto mais para o centro, mais importante e estável é o código.
Clean Architecture não é pasta, é mentalidade
Um erro comum de quem está começando é achar que Clean Architecture é só criar pastas chamadas domain, application e infrastructure. Isso ajuda, claro, mas o mais importante é como as dependências se relacionam.
Você pode usar Clean Architecture com NestJS, Express, Java, PHP ou qualquer outra tecnologia. O que muda é só a forma de implementar, não os princípios.
Clean Architecture no contexto do NestJS
O NestJS facilita bastante a aplicação da Clean Architecture porque já trabalha com conceitos como injeção de dependência, módulos, controllers e services. A diferença é que, em vez de colocar toda a lógica dentro dos services, você passa a separar melhor o que é regra de negócio e o que é detalhe técnico.
Por que usar Clean Architecture?
Talvez, neste ponto, você esteja se perguntando: “Ok, entendi o conceito, mas por que eu realmente deveria usar Clean Architecture no meu projeto?” Essa é uma dúvida muito comum, principalmente para quem está começando e acha que arquitetura é algo “grande demais” para aplicações pequenas. A verdade é que a Clean Architecture resolve dores reais do dia a dia do desenvolvedor.
Código mais fácil de entender e manter
Quando o projeto cresce sem uma estrutura clara, qualquer mudança vira um risco. Uma simples alteração pode quebrar funcionalidades que nem têm relação direta com o que você está mexendo. Com a Clean Architecture, cada parte do sistema tem uma responsabilidade bem definida. Isso faz com que o código fique mais legível e previsível, mesmo para alguém que nunca trabalhou naquele projeto.
Você sabe exatamente onde procurar uma regra de negócio, um caso de uso ou um detalhe de infraestrutura.
Menos acoplamento, mais flexibilidade
Um dos maiores benefícios da Clean Architecture é a redução do acoplamento. A lógica de negócio não depende do banco de dados, do framework ou da forma como os dados chegam pela API. Isso significa que você pode trocar um banco relacional por NoSQL, mudar de REST para GraphQL ou até migrar de framework sem precisar reescrever tudo.
No NestJS, por exemplo, isso fica muito claro quando você cria interfaces no domínio e implementa essas interfaces na camada de infraestrutura.
Testes muito mais simples
Testar aplicações mal estruturadas costuma ser difícil e demorado. Já com Clean Architecture, os testes ficam mais simples porque a regra de negócio está isolada. Você consegue testar casos de uso sem subir servidor, sem banco de dados e sem mocks complexos.
Isso aumenta a confiabilidade do sistema e acelera o desenvolvimento, principalmente em times.
Código mais profissional e valorizado
Arquitetura é um diferencial forte no mercado. Projetos bem estruturados passam mais confiança, facilitam o trabalho em equipe e são muito valorizados em entrevistas técnicas. Mostrar que você entende e aplica Clean Architecture com NestJS demonstra maturidade como desenvolvedor.
Entidades e Camada de Domínio
Se a Clean Architecture fosse um sistema solar, a camada de domínio seria o sol. Tudo gira em torno dela. É aqui que ficam as entidades, ou seja, as regras de negócio mais importantes da aplicação. Essa camada não sabe nada sobre banco de dados, HTTP, NestJS ou qualquer outro detalhe técnico. Ela existe para representar o problema que o software está resolvendo.
O que são entidades?
Entidades são objetos que representam conceitos reais do negócio e que possuem comportamento, não apenas dados. Um erro comum é tratar entidades como simples DTOs ou modelos de banco de dados. Na Clean Architecture, entidades têm lógica, validações e regras próprias.
Por exemplo, imagine um sistema simples de usuários. Um usuário não é apenas um conjunto de campos como name e email. Ele pode ter regras, como: um e-mail precisa ser válido, um usuário pode ou não estar ativo, etc.
Exemplo de entidade em TypeScript
export class User {
constructor(
public readonly id: string,
public name: string,
public email: string,
private active: boolean = true,
) {}
deactivate() {
this.active = false;
}
isActive(): boolean {
return this.active;
}
}
Perceba que essa classe não importa nada do NestJS. Ela é completamente independente e poderia ser usada em qualquer tipo de aplicação.
Domínio não depende de nada externo
Essa é uma das regras mais importantes da Clean Architecture. A camada de domínio não deve depender de frameworks, bibliotecas externas ou infraestrutura. Isso garante que as regras de negócio permaneçam estáveis mesmo quando todo o resto muda.
No contexto do NestJS, isso significa que suas entidades não devem ter decorators como @Entity(), @Column() ou @Injectable(). Esses detalhes pertencem a outras camadas.
Por que isso é tão importante?
Quando o domínio está isolado, você ganha liberdade. Pode mudar o banco de dados, refatorar controllers ou até trocar o framework sem tocar nas regras de negócio. Além disso, essa separação torna os testes muito mais simples e rápidos.
Casos de Uso (Use Cases)
Depois de entender o domínio e as entidades, chegamos a uma das partes mais importantes da Clean Architecture: os Casos de Uso, também chamados de Use Cases. Se as entidades representam as regras mais puras do negócio, os casos de uso representam o que o sistema faz.
Um caso de uso descreve uma ação específica da aplicação, como: criar um usuário, atualizar um perfil, listar pedidos ou cancelar uma assinatura. Ele orquestra as entidades e define o fluxo da regra de negócio, sem se preocupar com detalhes técnicos.
Qual o papel dos casos de uso?
O caso de uso é responsável por:
- Receber dados de entrada
- Validar regras de negócio
- Coordenar entidades
- Decidir o que acontece em cada cenário
Ele não sabe de onde os dados vieram (HTTP, fila, cron) e não sabe como os dados serão salvos (banco, API externa, cache). Ele apenas define o comportamento da aplicação.
Exemplo de caso de uso
Vamos imaginar um caso de uso para criar um usuário. Primeiro, criamos uma interface que representa o repositório:
export interface UserRepository {
save(user: User): Promise<void>;
findByEmail(email: string): Promise<User | null>;
}
Agora o caso de uso:
export class CreateUserUseCase {
constructor(private readonly userRepository: UserRepository) {}
async execute(name: string, email: string): Promise<User> {
const userExists = await this.userRepository.findByEmail(email);
if (userExists) {
throw new Error('Usuário já existe');
}
const user = new User(
crypto.randomUUID(),
name,
email,
);
await this.userRepository.save(user);
return user;
}
}
Note que esse código não depende de NestJS, banco de dados ou ORM. Ele depende apenas de interfaces, o que mantém o acoplamento baixo.
Por que separar casos de uso?
Essa separação deixa o sistema mais organizado e reutilizável. O mesmo caso de uso pode ser chamado por uma API REST, GraphQL ou até um job em background. Além disso, os testes ficam extremamente simples, pois você pode simular o repositório facilmente.
Adapters de Interface r5’jhm ,577yt6u
Os Adapters de Interface são a ponte entre o mundo externo e o núcleo da aplicação. É nessa camada que os dados entram e saem do sistema. Controllers, presenters, gateways e repositórios concretos vivem aqui. O papel dessa camada é simples: traduzir.
Ela traduz uma requisição HTTP em algo que o caso de uso entende e, depois, traduz a resposta do caso de uso para o formato que o cliente espera.
O que entra nessa camada?
Normalmente, aqui você encontra:
- Controllers (REST, GraphQL)
- DTOs
- Implementações de repositórios
- Mappers (Entity ↔ Model ↔ DTO)
Essa camada pode conhecer os casos de uso, mas os casos de uso nunca conhecem essa camada.
Exemplo com Controller no NestJS
Veja como um controller do NestJS se encaixa perfeitamente como adapter:
@Controller('users')
export class UserController {
constructor(
private readonly createUserUseCase: CreateUserUseCase,
) {}
@Post()
async create(@Body() body: CreateUserDto) {
const user = await this.createUserUseCase.execute(
body.name,
body.email,
);
return {
id: user.id,
name: user.name,
email: user.email,
};
}
}
O controller não tem regra de negócio. Ele apenas:
- Recebe dados da requisição
- Chama o caso de uso
- Retorna a resposta formatada
Implementação de repositório
Aqui também entram as implementações concretas das interfaces definidas no domínio:
@Injectable()
export class UserRepositoryPrisma implements UserRepository {
async save(user: User): Promise<void> {
// código específico do Prisma
}
async findByEmail(email: string): Promise<User | null> {
// código específico do Prisma
return null;
}
}
Perceba que o Prisma está isolado nessa camada. Se amanhã você trocar o ORM, o domínio e os casos de uso continuam intactos.
Benefícios dessa camada
Essa separação deixa claro onde estão os detalhes técnicos e evita que eles vazem para o domínio. Isso reduz o impacto de mudanças e mantém a aplicação organizada.
Frameworks e Infraestrutura
A camada de Frameworks e Infraestrutura é a parte mais externa da Clean Architecture. Aqui ficam todos os detalhes técnicos que, apesar de importantes, não devem influenciar as regras de negócio. Estamos falando de frameworks, banco de dados, ORMs, servidores HTTP, filas, cache, bibliotecas externas e qualquer outra dependência de tecnologia.
No caso do NestJS, essa camada inclui o próprio framework, módulos, configurações, providers, conexões com banco de dados e integrações externas.
O papel dessa camada
Essa camada existe para:
- Conectar o sistema ao mundo externo
- Implementar contratos definidos no domínio
- Configurar dependências e injeção
Ela pode mudar com frequência, sem causar impacto direto no núcleo da aplicação.
Exemplo com módulo do NestJS
É no módulo que fazemos a “cola” entre as camadas:
@Module({
controllers: [UserController],
providers: [
CreateUserUseCase,
{
provide: 'UserRepository',
useClass: UserRepositoryPrisma,
},
],
})
export class UserModule {}
Aqui o NestJS resolve a injeção de dependência, ligando a interface do domínio à implementação concreta. Essa configuração fica isolada na infraestrutura.
Banco de dados como detalhe
Na Clean Architecture, o banco de dados é apenas um detalhe. Ele não define como o sistema funciona, apenas onde os dados são armazenados. Isso muda totalmente a forma de pensar o projeto e evita que o ORM dite a arquitetura da aplicação.
Por que deixar essa camada por último?
Porque ela depende de tudo, mas ninguém depende dela. Isso garante liberdade para evoluir o sistema e facilita refatorações grandes sem medo de quebrar regras críticas do negócio.
Estrutura de Pastas Sugerida para Clean Architecture com NestJS
Uma dúvida muito comum é: “Como organizar as pastas de um projeto usando Clean Architecture?” Aqui é importante reforçar algo que já falamos antes: Clean Architecture não é sobre pastas, é sobre dependências. Ainda assim, uma boa estrutura ajuda muito na leitura, manutenção e escalabilidade do projeto.
Abaixo está uma sugestão simples e bem usada em projetos reais com NestJS.
Exemplo de estrutura de pastas
src
├── domain
│ ├── entities
│ │ └── user.entity.ts
│ ├── repositories
│ │ └── user.repository.ts
│ └── errors
│ └── user-already-exists.error.ts
│
├── application
│ ├── use-cases
│ │ └── create-user.use-case.ts
│ └── dtos
│ └── create-user.input.ts
│
├── infrastructure
│ ├── http
│ │ ├── controllers
│ │ │ └── user.controller.ts
│ │ └── dtos
│ │ └── create-user.dto.ts
│ ├── database
│ │ └── prisma
│ │ └── user.repository.prisma.ts
│ └── modules
│ └── user.module.ts
│
└── main.ts
Como ler essa estrutura
- domain: núcleo do sistema, regras de negócio puras
- application: casos de uso e lógica da aplicação
- infrastructure: NestJS, banco, HTTP e detalhes técnicos
Essa separação deixa claro onde cada coisa deve ficar. Se você estiver em dúvida sobre onde colocar um arquivo, a pergunta é simples: isso é regra de negócio ou detalhe técnico?
Evitando o “service gigante”
No NestJS tradicional, é comum concentrar tudo dentro de services. Com Clean Architecture, os services do framework se tornam apenas pontos de ligação, enquanto a lógica real fica nos casos de uso.
Isso evita classes gigantes, difíceis de testar e manter.
Dica para projetos menores
Em projetos pequenos, você pode simplificar essa estrutura sem perder os princípios. O importante é manter o domínio isolado e evitar dependências erradas.
Vantagens e Desvantagens da Clean Architecture
Apesar de todos os benefícios, a Clean Architecture não é uma bala de prata. Como qualquer abordagem, ela tem vantagens claras e desvantagens que precisam ser consideradas. Entender isso evita frustração e ajuda a tomar decisões mais maduras como desenvolvedor.
Vantagens da Clean Architecture
A principal vantagem é a organização do código. Projetos estruturados com Clean Architecture são mais fáceis de entender, manter e evoluir. Quando alguém novo entra no time, o aprendizado é mais rápido porque existe um padrão claro.
Outra grande vantagem é a testabilidade. Como as regras de negócio estão isoladas, é possível testar casos de uso e entidades sem depender de banco de dados ou servidor. Isso reduz o tempo de feedback e aumenta a qualidade do software.
A flexibilidade também se destaca. Trocar frameworks, ORMs ou até o tipo de interface não exige mudanças no domínio. Isso é extremamente valioso em projetos de longo prazo.
Além disso, a Clean Architecture força o desenvolvedor a pensar no problema antes da tecnologia, o que resulta em soluções mais bem modeladas e menos improvisadas.
Desvantagens e cuidados
A principal desvantagem é a complexidade inicial. Para projetos muito pequenos ou provas de conceito, a Clean Architecture pode parecer exagerada. Existe mais código, mais arquivos e mais abstrações.
Outro ponto é a curva de aprendizado. Para quem nunca trabalhou com arquitetura, conceitos como inversão de dependência e casos de uso podem parecer confusos no início.
Também é comum cair no erro de overengineering, criando abstrações desnecessárias. Clean Architecture não significa criar dezenas de interfaces sem motivo.
Quando usar Clean Architecture?
Ela faz mais sentido em:
- Projetos médios e grandes
- Sistemas que vão evoluir com o tempo
- Aplicações com regras de negócio complexas
- Times que valorizam testes e qualidade de código
Erros Comuns ao Usar Clean Architecture (e Como Evitá-los)
Quando alguém começa a aplicar Clean Architecture, é muito comum cometer alguns erros que acabam gerando mais confusão do que benefícios. A maioria desses problemas não vem da arquitetura em si, mas da forma como ela é interpretada e aplicada.
Confundir Clean Architecture com padrão de pastas
Um dos erros mais comuns é achar que basta criar pastas como domain, application e infrastructure para estar usando Clean Architecture. Isso, sozinho, não resolve nada. O mais importante é respeitar a direção das dependências.
Se o domínio importa algo do NestJS ou do ORM, a arquitetura já foi quebrada, mesmo que as pastas estejam “certas”.
Colocar regra de negócio no controller ou no service
No NestJS, é muito comum colocar toda a lógica dentro de services. Com Clean Architecture, isso muda. Controllers e services do framework devem ser finos, quase “burros”. Toda regra de negócio deve estar nas entidades ou nos casos de uso.
Se o controller começa a ter if, validações complexas e regras, é um sinal de alerta.
Criar interfaces demais
Abstração é importante, mas em excesso atrapalha. Nem tudo precisa de uma interface. Use interfaces principalmente quando:
- Existe mais de uma possível implementação
- Você quer isolar um detalhe técnico
- O código precisa ser testável
Criar interfaces sem necessidade torna o código difícil de navegar.
Ignorar testes
Clean Architecture facilita testes, mas isso só traz valor se você realmente testar. Um erro comum é montar toda a arquitetura e continuar sem testes automatizados. A arquitetura prepara o terreno, mas o teste é o que garante qualidade.
Forçar Clean Architecture em todo projeto
Nem todo projeto precisa disso. Aplicações simples, APIs pequenas ou MVPs podem funcionar melhor com uma estrutura mais direta. Clean Architecture é uma ferramenta, não uma regra absoluta.
Conclusão
A Clean Architecture não é sobre escrever mais código ou complicar projetos. Ela é sobre escrever código melhor, com mais clareza, menos acoplamento e mais foco no que realmente importa: a regra de negócio. Ao longo deste artigo, vimos que separar responsabilidades, respeitar a direção das dependências e isolar detalhes técnicos traz benefícios reais no dia a dia do desenvolvimento.
Usando NestJS, aplicar Clean Architecture se torna ainda mais natural, já que o framework oferece ferramentas como injeção de dependência, módulos e controllers que se encaixam perfeitamente nesse modelo. Quando o domínio está bem definido, os casos de uso organizados e a infraestrutura isolada, o projeto ganha vida própria e fica preparado para crescer sem virar um caos.
É importante lembrar que Clean Architecture não é uma receita pronta. Ela exige bom senso. Em projetos pequenos, talvez não faça sentido aplicar tudo desde o início. Já em sistemas que vão evoluir, ter essa base faz toda a diferença a médio e longo prazo.
Se você está buscando se tornar um desenvolvedor mais profissional, melhorar a qualidade do seu código ou se destacar em entrevistas técnicas, entender e aplicar Clean Architecture é um passo importante. Comece aos poucos, questione onde está colocando suas regras de negócio e observe como o código fica mais limpo com o tempo.
Agora quero saber de você: você já tentou aplicar Clean Architecture em algum projeto? Teve dificuldades ou conseguiu bons resultados? Deixa seu comentário aqui no CulturaDev e vamos trocar ideia sobre isso.