← Voltar ao blog Arquitetura

Arquitetura Limpa: princípios na prática

Publicado em 30/01/2026 • 14 min de leitura
Arquitetura Limpa: princípios na prática

Em 2012, Robert C. Martin (Uncle Bob) públicou um artigo que consólidaria ideias de diversas arquiteturas anteriores, como Hexagonal Architecture, Onion Architecture e BCE, em um modelo unificado que ele chamou de Clean Architecture. O conceito rapidamente ganhou tração na comunidade de desenvolvimento e se tornou referência para a construção de sistemas maintainaveis, testáveis e independentes de frameworks.

Mas o que exatamente significa "arquitetura limpa" na prática? Como aplicar esses princípios em um projeto real sem cair na armadilha da sobre-engenharia? Neste artigo, vamos desmistificar a Clean Architecture, explorar seus princípios fundamentais e mostrar como ela pode ser adaptada a diferentes contextos e tamanhos de projeto.

O problema que a Clean Architecture resolve

Todo software começa simples. Um arquivo, algumas funções, um banco de dados. Mas a medida que o sistema cresce, a complexidade se acumula. Regras de negócio se misturam com código de infraestrutura. Mudançar o banco de dados requer alterar dezenas de arquivos. Testes se tornam lentos e frágeis porque dependem de serviços externos. O sistema se torna rígido, frágil é difícil de evoluir.

A Clean Architecture propoe uma solução baseada em um princípio central: as regras de negócio são o ativo mais valioso do software e devem ser protegidas de detalhes de implementação como frameworks, bancos de dados e interfaces de usuário. Essa separação não e apenas uma questão estetica; ela tem implicações práticas profundas na testabilidade, manutenibilidade e longevidade do sistema.

Os circulos concentricos

A representação mais iconica da Clean Architecture e o diagrama de circulos concentricos, onde cada circulo representa um nível de abstracação diferente. A regra fundamental é simples: dependências sempre apontam para dentro. Camadas externas podem conhecer camadas internas, mas nunca o contrario.

Entidades (Entities)

No núcleo mais interno estão as entidades. Elas encapsulam as regras de negócio mais críticas e universais da organização. Uma entidade não sabe se está sendo usada em uma aplicação web, mobile ou CLI. Ela não sabe se os dados vem de um banco PostgreSQL ou de uma API REST. Ela simplesmente encapsula lógica de negócio pura.

Por exemplo, uma entidade Projeto pode conter regras como "um projeto deve ter pelo menos um membro" ou "o status de um projeto so pode mudar de 'planejamento' para 'em andamento', nunca direto para 'concluído'". Essas regras são verdadeiras independente de como o projeto e armazenado ou exibido.

Casos de uso (Use Cases)

A segunda camada contem os casos de uso da aplicação. Eles orquestram o fluxo de dados entre as entidades e direcionam as ações do sistema. Um caso de usó como CriarTarefa pode validar dados de entrada, verificar permissões, criar a entidade tarefa, persisti-la e notificar membros relevantes.

Casos de uso são específicos da aplicação (diferente das entidades, que são universais). Eles definem o que o sistema faz, mas não como faz em termos de infraestrutura. Um caso de uso pode dizer "notifique o usuário", mas não sabe se a notificação sera por email, push notification ou WebSocket.

Adaptadores de interface (Interface Adapters)

A terceira camada converte dados entre o formato mais conveniente para casos de uso e entidades e o formato mais conveniente para agentes externos como banco de dados e interfaces web. Aqui vivem controllers, presenters, gateways e repositories.

Um controller recebe uma requisicao HTTP, extrai os dados relevantes, converte-os para o formato esperado pelo caso de uso e invoca a operação. Um repository implementa a interface definida pela camada de casos de uso, traduzindo operações de domínio em queries de banco de dados.

Frameworks e drivers (Frameworks & Drivers)

A camada mais externa contem os detalhes concretos: o framework web (Express, NestJS, Spring), o ORM (Prisma, TypeORM), o banco de dados (PostgreSQL, MySQL, MongoDB), bibliotecas de UI e qualquer outro detalhe de implementação. Essa camada deve ser tratada como descartavel; trocar o framework ou o banco de dados deve ser possível sem afetar as regras de negócio.

"A arquitetura de um sistema e definida por suas fronteiras, não por seus frameworks. Frameworks são detalhes." - Robert C. Martin

O princípio da inversão de dependência

A cola que mantem toda a Clean Architecture funcionando e o Princípio da Inversão de Dependência (DIP), o "D" do SOLID. Em termos simples: módulos de alto nível não devem depender de módulos de baixo nível; ambos devem depender de abstrações.

Na prática, isso significa que o caso de uso CriarTarefa não depende diretamente do PrismaRepository. Em vez disso, ele depende de uma interface TarefaRepository que define as operações necessárias. O PrismaRepository implementa essa interface na camada externa. Se amanhã você decidir trocar o Prisma por TypeORM, basta criar uma nova implementação da mesma interface.

Implementação prática com TypeScript

Em TypeScript, interfaces são o mecanismo natural para implementar a inversão de dependência. Defina interfaces no mesmo módulo dos casos de uso e implemente-as na camada de infraestrutura. Use injeção de dependência (disponível nativamente no NestJS ou via bibliotecas como tsyringe) para conectar as implementações concretas.

Clean Architecture em diferentes escalas

Um erro comum e aplicar todos os níveis da Clean Architecture em qualquer projeto, independente do tamanho ou complexidade. Um CRUD simples não precisa de quatro camadas de abstracação. A chave e adaptar os princípios ao contexto.

Projetos pequenos (1-3 devs)

Para projetos pequenos, foque nos princípios em vez da estrutura completa. Separe lógica de negócio de infraestrutura, mas não se preocupe em criar interfaces para cada dependência. Uma separação pragmatica em três camadas (domínio, aplicação, infraestrutura) geralmente e suficiente.

Projetos medios (4-10 devs)

A medida que a equipe cresce, a Clean Architecture mostra seu valor. Interfaces bem definidas entre camadas permitem que diferentes desenvolvedores trabalhem em paralelo sem conflitos. Casos de uso explícitamente definidos fácilitam a comúnicação entre frontend e backend sobre o que o sistema deve fazer.

Projetos grandes (10+ devs)

Em projetos grandes, considere combinar Clean Architecture com Domain-Driven Design (DDD). Bounded contexts ajudam a particionar o sistema em módulos independentes, cada um com sua própria arquitetura interna. Microsserviços podem ser definidos ao longo das fronteiras de bounded contexts.

Testabilidade: o benefício mais tangivel

Se você tivesse que escolher um único benefício da Clean Architecture, seria a testabilidade. Quando regras de negócio não dependem de frameworks ou bancos de dados, elas podem ser testadas com testes unitarios rápidos e confiáveis.

Um caso de uso pode ser testado fornecendo implementações mock dos repositories. Não é necessário subir um banco de dados, configurar um servidor HTTP ou mockar bibliotecas complexas. O teste verifica apenas a lógica de negócio, que e exatamente o que mais importa.

Piramide de testes na Clean Architecture

Padrões complementares

CQRS (Command Query Responsibility Segregation)

CQRS separa operações de leitura (queries) de operações de escrita (commands). Issó se alinha naturalmente com a Clean Architecture, onde commands correspondem a casos de usó que modificam estado e queries correspondem a casos de usó que apenas consultam dados. Em sistemas complexos, o lado de leitura pode até usar um modelo de dados otimizado diferente do lado de escrita.

Event Sourcing

Em vez de armazenar apenas o estado atual, Event Sourcing armazena todos os eventos que levaram ao estado atual. Isso proporciona auditoria completa, capacidade de reconstruir estado em qualquer ponto no tempo é fácilita a integração entre microsserviços. Combinado com Clean Architecture, os eventos são gerados pelas entidades de domínio e persistidos pela camada de infraestrutura.

Repository Pattern

O padrão Repository e quase onipresente em implementações de Clean Architecture. Ele abstrai a persistência de dados atraves de uma interface que expoe operações de domínio (como buscarPorStatus em vez de findByColumn). O repository e definido na camada de domínio/aplicação e implementado na camada de infraestrutura.

Erros comuns na implementação

Anemia de domínio

Entidades que são apenas containers de dados, sem lógica de negócio. Se suas entidades são apenas getters e setters, toda a lógica de negócio provavelmente está nos casos de uso ou, pior, nos controllers. Mova regras de negócio para as entidades onde elas pertencem.

Excesso de abstrações

Criar interfaces para absolutamente tudo, mesmo quando ha apenas uma implementação e a chance de troca e nula. Cada abstracação tem um custo cognitivo. Use interfaces onde elas trazem benefício real (testabilidade, flexibilidade) e evite-as onde são apenas burocracia.

Mapeamento excessivo

Criar DTOs para cada fronteira entre camadas, resultando em dezenas de classes que apenas copiam dados de um formato para outro. Use mapeamento apenas onde ha transformação real de dados ou onde a proteção de fronteira e genuinamente necessária.

Ignorar o pragmatismo

Seguir a arquitetura dogmaticamente, mesmo quando o custo supera o benefício. A Clean Architecture e um guia, não uma religiao. Em um endpoint simples de CRUD, não faz sentido criar quatro camadas de abstracação. Reserve a complexidade arquitetural para a complexidade de negócio.

Clean Architecture no ecossistema Node.js

No ecossistema Node.js/TypeScript, o NestJS se destaca como um framework que naturalmente favorece a Clean Architecture. Seu sistema de módulos, injeção de dependência nativa e decorators fácilitam a implementação de camadas e inversão de dependência.

Projetos como o GalagoWork, construídos com NestJS e Prisma, se beneficiam dessa abordagem ao separar regras de negócio (como permissões de acesso e fluxos de trabalho em quadros Kanban) da infraestrutura (banco de dados Aurora MySQL, WebSockets para notificações em tempo real). Essa separação permite que novas funcionalidades sejam adicionadas sem risco de quebrar funcionalidades existentes.

Conclusão

A Clean Architecture não e sobre seguir um diagrama de circulos ao pe da letra. E sobre internalizar o princípio de que regras de negócio devem ser independentes de detalhes de implementação. Quando você protege seu domínio, ganha testabilidade, flexibilidade e a capacidade de evoluir o sistema por anos sem reescrita completa.

Comece identificando as regras de negócio do seu sistema e separando-as do código de infraestrutura. Use interfaces para definir fronteiras claras. Teste suas regras de negócio sem dependências externas. Adapte o nível de formalidade ao tamanho e complexidade do seu projeto. O investimento em uma boa arquitetura se paga exponencialmente ao longo do tempo.

Experimente o GalagoWork gratuitamente

Gestão de projetos com Kanban, integração GitHub e notificações em tempo real.

Começar grátis