Arquitetura Limpa (Clean Architecture)
Em sua essência, o modelo de arquitetura de software projetado em camadas tem como objetivo a separação das responsabilidades de um sistema. O principal objetivo consiste em separar o coração do software, ou seja, a implementação das regras de negócio de todo o restante do sistema, como acesso ao banco de dados, interface do usuário e outros serviços externos.
Essa separação em camadas permite que possamos trocar frameworks obsoletos ou no futuro adotar novas tecnologias que fazem mais sentido para a solução sem que isso afete nossa implementação de domínio. Além disso, esta separação promove o desacoplamento entres os módulos, facilitando a implementação de testes unitários e de integração.
Nas últimas décadas, diversos autores propuseram formas de realizar essa separação. Apesar das diferenças entre cada proposta, todas convergem para a separação de responsabilidades a fim de obter independência do domínio de qualquer agente externo. Em 2017, Robert C. Martin apresentou o conceito de Clean Architecture. Essa arquitetura tem como objetivo de integralizar as arquiteturas propostas em um modelo único:

Essa definição em camadas concêntricas define um fluxo de execução ordenado e direcional, que nos permite direcionar as dependências em um sentido único. Camadas internas não tem conhecimento das camadas externas. Essa separação isola as camadas internas de alterações nas camadas mais externas. Por exemplo, nossa camada de domínio (Entities) não será afetada caso optemos por utilizar outro framework de apresentação de dados, ou até mesmo outro banco de dados. Assim conseguimos proteger o mais importante da nossa aplicação: as regras de negócio.
If you think good architecture is expensive, try bad architecture. – Brian Foote
Abaixo segue uma breve explicação das responsabilidades de cada camada:
Domínio (Entities)
Nessa camada onde as regras de negócio serão implementadas. Podemos dizer que essa camada é a mais importante pois lida com o “coração” do software. Toda regra de negócio é implementada nessa camada. Nessa camada estão presentes as entidades de negócios com características (propriedades) e comportamento (métodos). Devemos evitar ao máximo a definição de entidades anêmicas, pois perdemos a vantagem de expressar comportamento e razões para mudanças dos modelos de domínio. Uma entidade anêmica que expõe todas as suas propriedades de forma pública não tem controle de quem a modificou e nem por que. Além disso, quando deixamos as características dos nossos modelos apenas leitura, permitindo que apenas construtores e métodos alterarem os dados, temos a segurança de sempre trabalhar com um instância válida do modelo.
Casos de uso (Use cases)
Nessa camada encontramos implementações de casos de uso específicos da aplicação. Geralmente essa camada recebe abstrações de acesso a serviços externos, como por exemplo, acesso a banco de dados, serviço de e-mail, etc. Toda orquestração de serviços e entidades de negócio necessária para atender um caso de uso específico é de responsabilidade desta camada. Devido a separação e fluxo de dependências, alterações nessa camada não refletem na camada de domínio (Entities).
Adaptadores (Interface Adapters)
Essa camada serve como uma medição dos dados trafegados entre sistemas externos e os casos de uso. A responsabilidade dessa camada é receber os dados de uma fonte externa e fazer a mediação destes dados para os casos de uso e vice-versa. Nesta camada podemos utilizar o padrão Mediator para desacoplar ainda mais da camada de casos de uso, onde podemos receber dados através de um Command e devolvê-los através de uma DTO.
Interfaces, Api’s e Banco de dados (Frameworks & Drivers)
Por último, temos a camada onde são definidas ferramentas externas, como banco de dados, interface do usuário, etc. A ideia dessa camada é não conter muita implementação. Podemos realizar nessa camada toda a inicialização e injeção das dependências que serão utilizadas nas camadas inferiores.
Onde lançar exceções de negócio?
Exceções de negócio que validam apenas uma entidade podem ser lançadas nos métodos de comportamento da própria entidade. Em situações onde precisamos de diversas entidades ou informações externas para validar o caso de uso, a validação pode e deve ser implementada na camada de casos de uso.
Qualquer projeto pode fazer o uso do Clean Architecture?
A adoção do padrão Clean Architecture, sem dúvidas, adiciona uma complexidade acidental ao projeto. Para projetos “CRUDS” no qual só servem como intermediador para algum banco de dados, ou até projetos com um lifetime pré-determinado, o uso do Clean Architecture pode adicionar mais tempo de desenvolvimento sem nenhum benefício tangível.
Para projetos pequenos mas com perspectiva de crescimento, o uso do Clean Architecture pode guiar o time para a implementação de uma arquitetura robusta e escalável no qual pode demandar um tempo maior no início do desenvolvimento, mas que com certeza facilitará a implementação de novas features e evitará grandes dores de cabeça no futuro.
Conclusão
Dessa maneira, a adoção do padrão Clean Architecture no início do seu projeto pode trazer grandes benefícios no futuro. Entretanto, é indispensável uma análise e um entendimento profundo de como esse padrão pode auxiliar o seu time a projetar uma arquitetura sólida e robusta que atenda os requisitos de negócio. Lembre-se, toda complexidade acidental precisa agregar valor ao negócio de alguma forma, caso contrário, não existem motivos tangíveis para a utlização da mesma.