Mitos e Verdades sobre Clean Architecture: Guia Prático para Desenvolvedores

Mitos e Verdades sobre Clean Architecture: Guia Prático para Desenvolvedores





Mitos e verdades sobre Clean Architecture



O que é Clean Architecture e por que isso importa

Clean Architecture é uma abordagem que organiza o código em camadas concêntricas, onde as regras de negócio residem no núcleo e dependem apenas de abstrações. A direção das dependências deve ir do exterior para o interior: UI, frameworks, bancos de dados, serviços externos podem evoluir sem romper a lógica de domínio.

O objetivo é criar código fácil de testar, manter e evoluir, reduzindo o acoplamento entre domínios e infraestrutura. Ao separar responsabilidades, o núcleo da aplicação se mantém estável diante de mudanças tecnológicas.

Em prática, isso se traduz em entidades de domínio, casos de uso (application services), interfaces (ports) para comunicação com o que fica fora do núcleo e adaptadores (adapters) para converter dados entre as camadas.

Mitos comuns e a verdade técnica por trás deles

  • Mito: Clean Architecture é obrigatório apenas para grandes sistemas.

    Verdade: os benefícios aparecem já em protótipos e aplicações modestas: melhor separação de responsabilidades, mais testes e menor atrito para evoluções futuras.
  • Mito: Precisa de muitas camadas para ser válida.

    Verdade: a ideia é separar responsabilidades com clareza. a quantidade de camadas deve refletir o domínio; mais nem sempre é melhor.
  • Mito: O núcleo não pode conhecer infraestrutura de forma alguma.

    Verdade: o núcleo define interfaces (ports) que a infraestrutura implementa. O núcleo não depende de detalhes, mas consome abstrações.
  • Mito: Arquitetura limpa substitui testes.

    Verdade: a prática favorece testes mais estáveis (use cases testáveis com dependências simuladas) e facilita a validação de regras de negócio.

Princípios, padrões e boas práticas que guiam a implementação

  • dependências apontam para dentro. O domínio não deve depender de detalhes externos.
  • define contratos (interfaces) para comunicação entre o núcleo e o exterior, que é implementado por adaptadores.
  • cada camada expõe interfaces simples e bem definidas; dados trafegam por DTOs/VOs entre camadas.
  • o código de alto nível não depende de código de baixo nível; ambos dependem de abstrações.
  • a separação facilita mocks/stubs e testes de domínio sem objetos reais de infra.

Guia prático de implementação: começando com uma refatoração incremental

  1. Identifique o domínio mínimo e crie entidades simples que representam o negócio.
  2. Defina use cases que operam sobre esse domínio, mantendo-os independentes de qualquer infraestrutura.
  3. Crie ports (interfaces) para persistência, notificações, e integrações externas. A infraestrutura implementa esses ports.
  4. Implemente adapters para converter dados entre o núcleo e infra (DTOs, mappers, mapeamento de fronteira).
  5. Refatore aos poucos, mantendo o núcleo estável e com testes cobrindo casos de uso essenciais.

Exemplo simples em TypeScript

Este trecho demonstra a dependência apenas de interfaces no núcleo. A implementação de repositório ficaria na camada de infraestrutura.

// Domain & Use Case (núcleo, sem dependência de infraestrutura)
export class Order {
  constructor(public id: string, public items: string[]) {}
}

export interface IOrderRepository {
  save(order: Order): Promise;
  find(id: string): Promise;
}

export class CreateOrder {
  constructor(private repo: IOrderRepository) {}

  async execute(input: { items: string[]; customerId: string }): Promise {
    const id = generateId();
    const order = new Order(id, input.items);
    await this.repo.save(order);
    return id;
  }
}

function generateId(): string {
  return Math.random().toString(36).substring(2, 9);
}

Observação: a implementação real de IOrderRepository fica na camada de infraestrutura e depende de banco de dados ou serviços externos. O núcleo continua inalterado diante dessas mudanças.

Estrutura de pastas sugerida

  • src/domain/entities/Order.ts
  • src/domain/ports/IOrderRepository.ts
  • src/application/use-cases/CreateOrder.ts
  • src/application/port/… (outros ports)
  • src/infrastructure/repositories/OrderRepository.ts
  • src/infrastructure/database/… (drivers, configs)
  • src/adapters/controllers/CreateOrderController.ts

Conclusão prática

Clean Architecture não é uma fórmula mágica, mas uma forma de estruturar o código para evoluir com clareza. Foque em manter o núcleo estável, definir contratos estáveis entre camadas e evoluir a infraestrutura de forma isolada. Com isso, você obtém maior previsibilidade, facilidade de testes e menor risco de regressões ao longo do tempo.

Curtiu? Leve o conhecimento adiante

Este tema é apenas uma parte do que escrevo sobre design de software e arquitetura. Confira outros posts para aprofundar ainda mais seus conhecimentos: