Este repositório apresenta um API de cadastro de pessoas desenvolvido seguindo os princípios da Clean Architecture. O objetivo aqui é me ajudar na consolidação dos conceitos que venho estudando sobre esse tema de arquitetura limpa.
Adicionalmente, este estudo dirigido se apoia na proposta deste vídeo: Desenvolvendo API com Arquitetura Limpa: Aprenda na Prática!.
Para uma leitura mais aprofundada do tema, você pode acessar a publicação original do autor em The Clean Architecture.
Esta é a famosa imagem que o autor traz em sua publicação:
A ideia que a imagem acima apresenta é a divisão do software em camadas para separar as responsabilidades, com o intuito de se construir um software que contém as seguintes características:
- Independente de framework;
- Testável;
- Independente de UI;
- Independente de banco de dados;
- Independente de qualquer outro componente externo.
Algumas observações que gostaria de colocar:
- A quantidade de camadas acima é apenas uma sugestão, portando devemos avaliar à luz do contexto que estivermos escrevendo a aplicação;
- Não existe um passo a passo que nos guia do início ao fim para então julgarmos: isto é clean architecture, isso não é. Na verdade, encontramos por aí muitas variações, já que estão sujeitas ao contexto de negócio e à interpretação do desenvolvedor. A Hexagonal Architecture e a Onion Architecture são exemplos de interpretações que resultam numa arquitetura limpa. Apesar das variações a regra mais importante a ser seguida é a regra da dependência, que falaremos um pouco mais à frente.
As setas da imagem acima representa a regra da dependência que diz: camadas mais internas não devem ter dependência de camadas mais externas. Trazendo um exemplo para ajudar no entendimento, um repository da JPA, que pela divisão faz parte da camada Frameworks & Drivers, nunca deveria estar sendo importado dentro de um Use Case.
Entidades (Entities):
No contexto de grandes empresas, as entidades são objetos que contém regras de negócio abrangentes da organização. Em projetos simples como o deste repo, nós normalmente vemos classes que representam um POJO (Plain Old Java Object).
Casos de Uso (Use Cases):
É onde são implementadas as regras de negócio da aplicação.
Adaptadores de Interfaces (Interface Adapters)
Camada que converte os dados de forma mais adequada de algum componente externo como bancos de dados, WEB, para os casos de uso e entidades, e vice-versa.
Frameworks e Drivers
Camada onde ficam os detalhes de implementação de banco de dados, WEB, e o Framework. Detalhes do Framework Spring, por exemplo, só deveriam aparecer aqui.
À luz dos conceitos acima, vamos tentar identificar os elementos da arquitetura limpa no desenvolvimento desta aplicação.
A API possui apenas dois endpoints: um para cadastrar pessoa e outro para listar todas as pessoas cadastradas. Então aqui vai algumas observações:
- Não há swagger disponível. Os endpoints podem ser utilizados e seus contratos conhecidos utilizando a collection do postman disponível na pasta
postman-collection
; - O código fonte está na pasta
person-api
; - Não há nenhuma validação dos dados de entrada;
- O banco de dados utilizado aqui é o H2, podendo ser acessado pelo console em
http://localhost:8080/h2-console
; - O projeto requer Java 21 ou superior para ser executado.
com.rochards.registerapi.person
|
|-- core
| |-- entities
| | |-- Person.java
| |-- usecases
| | |-- CreatePersonUseCase.java
| | |-- CreatePersonUseCaseImpl.java
| | |-- FindAllPersonUseCase.java
| | |-- FindAllPersonUseCaseImpl.java
| |-- enums
| | |-- PersonType.java
| |-- exceptions
| | |-- BusinessException.java
| |-- gateways
| | |-- PersonGateway.java
|-- infrastructure
| |-- configuration
| |-- BeansConfig.java
| |-- controllers
| | |-- CreatePersonController.java
| | |-- FindPersonController.java
| |-- dtos
| | |-- PersonRequest.java
| | |-- PersonResponse.java
| |-- gateways
| | |- PersonRepositoryGateway.java
| |-- persistence
| | |- PersonModel.java
| | |- PersonRepository.java
|-- CleanArchPersonApiApplication.java
O pacote base core
concentra as regras de negócio da aplicação. Dois elementos da figura acima podem ser facilmente notados dentro dos pacotesentites
e usecases
. Os demais pacotes também fazem parte da regra negócio.
Um atenção especial ao pacote gateways
: este não representa um elemento da camada de Interface Adapters, mas é apenas para favorecer a inversão de dependência, uma vez que nossos casos de uso não devem importar classes com implementações concretas dessas operações. Elementos de camadas mais externas vão de fato prover implementações concretas para as interfaces declaradas aqui.
O que podemos afirmar com certeza é que nenhuma classe do pacote base core
está importando algo de alguma camada mais externa, respeitando assim a regra da dependência.
Com o entendimento atual que tenho da proposta da arquitetura limpa, creio que este pacote não está estruturado de uma forma a respeitar as camadas sugeridas pelo autor. Por exemplo, os controllers estão claramente dependendo de anotações do Framework Spring. Particularmente não vejo mal algum esse cruzamente de dependências acontecendo nesta chamada.
Se possui dúvidas, críticas ou sugestões sobre o texto apresentado aqui, sinta-se livre para abrir um issue.