Introdução ao ArchUnit

No desenvolvimento de software, garantir que a arquitetura do projeto seja respeitada ao longo do tempo é fundamental para manter a qualidade e a facilidade de manutenção do código. Uma ferramenta que tem se destacado nesse aspecto é o ArchUnit, uma biblioteca Java para testar arquiteturas de forma declarativa e fácil. Neste post, vamos explorar como você pode utilizar o ArchUnit para garantir que sua equipe não utilize classes obsoletas como java.util.Date
e como manter a integridade de camadas em projetos Spring.
Configurando o ArchUnit
Antes de começarmos com os exemplos de testes, você precisará adicionar o ArchUnit ao seu projeto. Aqui está como você pode fazer isso usando Maven e Gradle:
Maven
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit</artifactId>
<version>1.3.0</version>
</dependency>
Gradle
testImplementation 'com.tngtech.archunit:archunit:1.3.0'
Essas dependências adicionam o ArchUnit apenas ao escopo de testes do seu projeto, o que é adequado, já que o ArchUnit é utilizado principalmente para escrever testes de arquitetura.
Evitando o Uso de java.util.Date
A classe java.util.Date
é considerada obsoleta em muitos casos, sendo recomendado o uso das classes do pacote java.time, como LocalDate. Com o ArchUnit, podemos criar testes para garantir que ninguém no projeto está utilizando java.util.Date. Veja um exemplo de como fazer isso:
package io.github.gasparbarancelli.blog;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.lang.ArchRule;
import org.junit.jupiter.api.Test;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
public class JavaUtilDataArchitectureTests {
@Test
public void noJavaUtilDateUsage() {
JavaClasses importedClasses = new ClassFileImporter().importPackages("io.github.gasparbarancelli.blog");
ArchRule rule = noClasses()
.should().dependOnClassesThat()
.resideInAPackage("java.util")
.andShould().haveSimpleName("Date");
rule.check(importedClasses);
}
}
Este teste irá falhar se qualquer classe no pacote especificado fizer referência direta à classe java.util.Date
.
Validando Camadas em Projetos Spring
Projetos Spring comumente utilizam uma divisão clara de responsabilidades entre Controllers, Services e layers de Repositories. Com o ArchUnit, você pode definir regras para garantir que essa estrutura seja respeitada. Veja como você pode fazer isso:
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.lang.ArchRule;
import static com.tngtech.archunit.library.Architectures.layeredArchitecture;
public class SpringLayerArchitectureTests {
@Test
public void validateLayerDependencies() {
JavaClasses importedClasses = new ClassFileImporter().importPackages("io.github.gasparbarancelli.blog");
ArchRule rule = layeredArchitecture()
.consideringAllDependencies()
.layer("Controller").definedBy("..controller..")
.layer("Service").definedBy("..service..")
.layer("Repository").definedBy("..repositories..")
.whereLayer("Controller").mayNotBeAccessedByAnyLayer()
.whereLayer("Service").mayOnlyBeAccessedByLayers("Controller")
.whereLayer("Repository").mayOnlyBeAccessedByLayers("Service");
rule.check(importedClasses);
}
}
Este teste assegura que cada camada só seja acessada por camadas superiores permitidas, mantendo assim a estrutura desejada e evitando acoplamentos indesejados.
Conclusão
O uso do ArchUnit para testar e validar a arquitetura de seu software pode ser uma ferramenta poderosa para manter a consistência e a qualidade do código ao longo do desenvolvimento. Com testes claros e objetivos, é possível evitar regressões arquiteturais e garantir que boas práticas sejam seguidas por todos os desenvolvedores da equipe.