Compre na Amazon, Submarino ou MercadoLivre.
Arquitetura Limpa (Clean Architecture) é um padrão arquitetural proposto por Robert Martin – mais conhecido como Uncle Bob – com o objetivo de promover a implementação de sistemas que favorecem reusabilidade de código, coesão, independência de tecnologia e testabilidade. Se quisermos adotar a classificação do Capítulo 7, a Arquitetura Limpa pode ser considerada como uma arquitetura em camadas.
Normalmente, Arquitetura Limpa é ilustrada por meio da seguinte figura (copiada de um post do The Clean Code Blog
).
A seguir, vamos comentar sobre cada um das camadas da arquitetura.
No centro da arquitetura, temos as classes responsáveis pelas regras de negócio, que podem ser de dois tipos: Entidades e Casos de Uso.
Entidades são classes comuns a vários sistemas da empresa. Suponha, por exemplo, uma universidade com sistemas acadêmico, financeiro, extensão, etc. Todos esses sistemas têm que lidar com classes como Aluno
, Professor
, Curso
, Departamento
, etc. Essas classes são então chamadas de Entidades. Além de dados, entidades podem implementar regras de negócio genéricas. Por exemplo, uma regra da universidade define que todo Professor
deve pertencer a exatamente um Departamento
.
Já as classes da camada Casos de Uso implementam regras de negócio um pouco menos genéricas e, normalmente, relativas a um único sistema. Por exemplo, o sistema acadêmico do nosso exemplo pode ter uma classe DiarioDeClasse
que armazena a lista de objetos do tipo Aluno
matriculados em uma Disciplina
que está sendo ofertada em um determinado semestre. Uma regra de negócio define que um Aluno
somente pode ser incluído em um DiarioDeClasse
se tiver cursado os pré-requisitos da sua Disciplina
.
Para evitar qualquer tipo de confusão, gostaríamos de comentar que os casos de uso em uma Arquitetura Limpa não tem uma correspondência, pelo menos direta, com casos de uso para especificação de requisitos e, menos ainda, com diagramas de casos de uso da UML, tal como estudamos no Capítulo 3.
Na terceira camada, de dentro para fora, temos classes e interfaces chamadas de Adaptadores. A função delas é converter dados de um formato para outro.
Suponha, por exemplo, que o sistema use uma API REST para comunicação com seus clientes. As classes adaptadoras serão então responsáveis por implementar os endpoints REST da API. Isto é, elas devem receber as requisições e encaminhá-las para os casos de uso correspondentes. E, também, fazer o caminho inverso: receber os resultados retornados pelos casos de uso e convertê-los em documentos JSON que serão enviados para os clientes.
Se o sistema for implementado usando-se um framework MVC Web, os controladores pertencerão a essa camada.
Na camada mais externa, temos classes pertencentes a bibliotecas e frameworks externos (de terceiros), as quais podem ser responsáveis por persistência, construção de interfaces com usuários, envio de mails, integração com outros sistemas, comunicação com um determinado hardware, etc.
Por exemplo, a universidade do nosso exemplo pode possuir um sistema para gerenciamento de cursos de extensão, o qual aceita pagamento por meio de cartões de crédito. Para isso, o sistema usa um serviço de pagamentos de terceiros, que oferece algumas classes para processamento de pagamentos. Logo, essa classes ficam na camada mais externa de uma Arquitetura Limpa.
No livro Arquitetura Limpa, veja como essa camada é descrita:
Todos os detalhes ficam na camada de frameworks e drivers. A Web é um detalhe. O banco de dados é um detalhe. Mantemos essas tecnologias na camada mais externa porque é onde elas podem fazer menos mal.
Em uma Arquitetura Limpa, as classes de uma camada X não devem conhecer nenhuma classe de uma camada Y mais externa. No seu livro, Uncle Bob afirma categoricamente:
O nome de um elemento declarado em uma camada externa não deve ser mencionado pelo código de uma camada interna. Isso inclui funções, classes, variáveis e qualquer outro elemento de código.
Assim, em uma Arquitetura Limpa, as camadas centrais são mais estáveis – menos sujeitas a mudanças – do que as camadas mais externas. Por exemplo, as entidades de um sistema raramente precisam ser modificadas. Sobre os casos de uso, é verdade que eles, às vezes, precisam ser mantidos. Porém, queremos evitar que essas mudanças sejam motivadas por mudanças nas tecnologias adotadas na aplicação, como bancos de dados, frameworks e bibliotecas.
Resumindo, a Regra de Dependência garante que as entidades e os casos de uso sejam classes limpas
de qualquer tecnologia ou serviço externo ao sistema.
Em uma Arquitetura Limpa, fluxos de controle de fora para dentro
são implementados de forma natural
, pois eles seguem o mesmo sentido da Regra de Dependência. Por exemplo, uma camada mais externa Y pode criar um objeto de um tipo mais interno X e então chamar um método desse objeto.
No entanto, em alguns cenários, um caso de uso pode ter que chamar um método de uma classe de uma camada mais externa. Para ficar claro, suponha que um caso de uso precise enviar um mail. Antes de mais nada, vamos supor que existe no sistema uma classe, de uma camada mais externa, chamada MailServiceImpl
e com um método send
:
public class MailServiceImpl {
public void send(String msg);
}
No entanto, veja que esse exemplo implica em um fluxo de fora para dentro: o caso de uso tem que declarar uma variável de uma classe de uma camada mais externa, o que contraria a regra da dependência!
A solução implica em ter uma interface na camada de caso de uso chamada MailServiceInterface
com um método send(String msg)
.
package CasosDeUso;
public interface MailServiceInterface {
void send(String msg);
}
// outras classes da camada Casos de Uso
Essa interface foi criada para funcionar como uma abstração para o serviço de envio de mail. Ou seja, para evitar que o caso de uso tenha que se acoplar a uma classe concreta desse serviço.
Além disso, como MailServiceInterface
pertence à camada Caso de Uso, as outras classes dessa camada podem chamar send
sem violar a Regra de Dependência.
Prosseguindo, a classe MailServiceImpl
deve implementar a interface MailServiceInterface
.
import CasosDeUso.MailServiceInterface;
public class MailServiceImpl implements MailServiceInterface {
public void send(String msg);
}
Essa implementação não viola a Regra de Dependência, pois uma classe de uma camada mais externa (MailServiceImpl
) está dependendo de um elemento de código de uma camada mais interna. No caso, esse elemento é uma interface (MailServiceInterface
).
O seguinte diagrama de classes ilustra a solução que acabamos de descrever.
Uma Arquitetura Limpa é uma aplicação de diversos conceitos que estudamos no Capítulo 5, incluindo propriedades de projeto como coesão, acoplamento e separação de interesses e princípios de projeto como responsabilidade única e inversão de dependências. Ela faz uso também do padrão de projeto adaptador, que estudamos no Capítulo 6.
As recomendações principais de uma Arquitetura Limpa são as seguintes:
Ao implementar uma aplicação, pense nas suas Entidades, que são classes que armazenam principalmente dados e que poderão ser reusadas em outros sistemas que você construir no futuro.
Depois, pense nos Casos de Uso, que vão implementar regras de negócio relacionadas com as Entidades de seu sistema. Mas torne as classes que representam Entidades e Casos de Uso limpas
de qualquer tecnologia. Lembre-se a Web é um detalhe; o banco de dados é um detalhe
.
Por fim, pense nas classes Adaptadoras, que vão funcionar como portas de entrada e saída, para comunicação entre as classes internas e o mundo externo.
Seguindo essas recomendações, você vai produzir uma arquitetura que separa dois tipos de interesses (ou requisitos): interesses de negócio e interesses de tecnologia. E assim, será mais fácil testar seu sistema e também adaptá-lo às novas tecnologias que, com certeza, vão surgir no futuro.
Para saber mais sobre Arquitetura Limpa, você pode consultar o livro de mesmo nome do Uncle Bob. O Prof. Otavio Lemos (UNIFESP) tem também uma lista interessante de vídeos no YouTube sobre o tema.
Em uma arquitetura limpa o nome de um elemento declarado em uma camada externa não deve ser mencionado pelo código de uma camada interna
? Qual a principal vantagem ou benefício dessa regra?
No texto do artigo, propositalmente não mencionamos a camada da classe MailServiceImpl
.
Se quisermos ter um código totalmente aderente aos princípios de uma Arquitetura Limpa, por que MailServiceImpl
não pode pertencer à camada de Adaptadores?
Em qual camada você implementaria então MailServiceImpl
?
Voltar para a lista de artigos.