Criada em meados de 2012 por Robert Cecil Martin (“Uncle Bob”), a Clean Architecture tem como principais finalidades ser independente de frameworks, facilmente testável, independentemente da interface do usuário, ou seja, a interface do usuário pode mudar à vontade sem que isso reflita no resto do sistema, ser independente de banco de dados, já que ela mantém todas as regras de negócio na própria aplicação e, por último, ser independente de qualquer agente externo, sendo que as regras de negócio não “enxergam” o “mundo exterior”. Resumindo, a Clean Architecture é um tipo de arquitetura de software amplamente independente.
Neste artigo, mostraremos o conceito dessa arquitetura e também um exemplo de como implementá-la no mundo real.
Clean Architecture
Dependências
Na imagem acima, temos o desenho de como a Clean Architecture funciona. Note que as setas horizontais da imagem que representam as dependências entre as camadas vêm de “fora para dentro”, ou seja, a camada Framework “enxerga” somente a Interface Adapters, que por sua vez “enxerga” somente a User Cases, que finalmente “enxerga” apenas a Entites. Esta é a principal regra da Clean Architecture, e talvez sua única: o Princípio da Dependência.
As camadas internas não devem ter qualquer dependência das externas, nem indiretas, como nomes de variáveis, funções ou termos.
Provavelmente você terá mais abstrações nas camadas internas e mais implementações nas externas, utilizando injeção de dependência para fazer tudo funcionar.
Entities
Camada que encapsula as entidades e regras de negócio. O principal entendimento aqui é que essa camada deve conter tudo que seja pertinente ao sistema como um todo de uma maneira mais genérica, ou seja, deve conter as regras que tenham menos possiblidades de mudança quando algo externo mudar, como algo na interface do usuário.
Em um grande sistema, que reusa código da empresa, aqui poderiam residir as bibliotecas que são consumidas pela aplicação, mas que a aplicação não irá modificar, como algum framework ou outros domínios de negócio. Em uma aplicação menor, podem ser apenas interfaces e classes que tenham que ser utilizadas em todas as camadas, como a interface de um repositório.
User cases
Essa camada é a que contém as regras de negócios mais específicas do sistema. É aqui que todos os casos de uso do sistema são implementados. É esperado que apenas mudanças de requisitos afetem essa camada, sendo que assim como na Entities, alterações nas camadas externas não devem afetar essa camada. (sugestão: essa camada. No entanto, assim como na Entities, alterações…)
Interface Adapters
Camada que tem como finalidade converter dados da maneira mais acessível e conveniente possível para as camadas Entities e User Cases. Um exemplo seria o uso do AutoMapper, onde eu poderia controlar as estruturas transmitidas entre User Cases e Entities com o interface do usuário, por exemplo.
Frameworks
Essa camada é composta por ferramentas como banco de dados, interface do usuário etc. Nessa camada, a ideia ée ter o mínimo de código possível, apenas o necessário para interligar as camadas e injetar as implementações necessárias nas camadas interiores.
Usando a Clean Architecture no mundo real
Para facilitarmos o entendimento, criamos um exemplo bem simples de um sistema escolar, no qual vamos cadastrar novas turmas. A estrutura base do projeto foi divida nas camadas: Core, Entities, UseCases e Web, conforme nos mostra a imagem abaixo:
A camada Core foi incluída a fim de ter um nível de abstração ainda maior do que a Entities. Essa camada possui apenas um repositório padrão (IRepository), um interactor que vai receber uma entrada e nos devolver uma resposta (IInteraction) e uma entidade que possui as propriedades que serão utilizadas por todas as classes (IEntity). Outra coisa interessante nessa camada é que ela contém todos os pacotes que serão utilizados pelas outras camadas, já que, conforme dito anteriormente, as dependências são de “fora para dentro”.
Na camada Entities, temos as Classes que de fato compõem o sistema – observe que ela conhece a camada Core, uma vez que ela é menos abstrata.
Na camada UseCases, possuímos todas as nossas regras de negócio. Observe que temos apenas três arquivos para a entidade Turma, sendo um arquivo de Input que vai receber os dados (CadastrarTurmaInput), um Interactor (CadastrarTurmaInteractor) que contém todas as regras de negócio implementadas – por exemplo, ao cadastrar uma Turma será que existe um professor disponível? Ou será que já não existe uma turma para mesma data? – e por último um arquivo de output (CadastrarTurmaOutput) que vai transformar os dados para que possa ser exibido. (possam ser exibidos? Fiquei em dúvida se isso se refere a dados)
Nossa última camada, a Web, vai apenas consumir os dados transformados pela camada anterior e exibi-los para o usuário. Ela também é responsável, nesta implementação simplificada, por injetar os repositórios para consumo nos interactors, e os interactors para consumo pelos controllers.
Conforme vimos no exemplo do sistema escolar, cada camada possui uma única responsabilidade sempre respeitando suas dependências, mantendo assim uma divisão total de responsabilidades, minimizando ao máximo qualquer alteração externa com interface com o usuário (de usuário?), banco de dados etc.
Caso queiram baixar o código utilizado no exemplo, ele pode ser encontrado através deste link.
Fonte: https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html
***
Artigo publicado na revista iMasters, edição #27: https://issuu.com/imasters/docs/imasters_27_issuu