====== 06 - Pruebas de arquitectura: ArchUnit ======
Una **prueba de arquitectura** comprueba que el código respeta las reglas estructurales acordadas: qué capa puede depender de cuál, dónde están las clases, si hay ciclos de dependencia. **ArchUnit** analiza el bytecode Java y convierte esas reglas en pruebas que fallan cuando el código las rompe.
===== 1. Por qué probar la arquitectura =====
El diagrama de clases del tema anterior muestra las dependencias; las pruebas de arquitectura las **imponen automáticamente**. Sin ellas, las reglas se cumplen mientras alguien las recuerda; con ellas, se comprueban en cada ''mvn test''.
@AnalyzeClasses(packages = "com.example.library")
class ArchitectureTest {
@ArchTest
void controllersDoNotDependOnPersistence() {
noClasses().that().resideInAPackage("..controller..")
.should().dependOnClassesThat()
.resideInAPackage("..persistence..")
.check();
}
}
La anotación ''@AnalyzeClasses'' indica qué paquetes analizar y ''check()'' ejecuta la regla sobre las clases importadas.
===== 2. Reglas de capas =====
ArchUnit describe la estructura por capas y sus dependencias permitidas:
@ArchTest
void respectsLayeredArchitecture() {
LayeredArchitecture layers = layeredArchitecture().consideringAllDependencies()
.layer("Controller").definedBy("..controller..")
.layer("Service").definedBy("..service..")
.layer("Persistence").definedBy("..persistence..");
layers.whereLayer("Controller").mayNotBeAccessedByAnyLayer()
.whereLayer("Service").mayOnlyBeAccessedByLayers("Controller")
.whereLayer("Persistence").mayOnlyBeAccessedByLayers("Service")
.checkClasses();
}
^ Regla ^ Significado ^
| ''mayOnlyBeAccessedByLayers'' | Qué capas pueden usar esta capa |
| ''mayNotBeAccessedByAnyLayer'' | Nadie la usa desde fuera; solo se accede hacia dentro |
===== 3. Otras reglas útiles =====
// Los servicios terminan en Service
classes().that().resideInAPackage("..service..")
.should().haveSimpleNameEndingWith("Service")
.check();
// Sin ciclos entre paquetes
slices().matching("com.example.(**)").should().beFreeOfCyclicDependencies()
.check();
// El dominio no depende del framework
noClasses().that().resideInAPackage("..domain..")
.should().dependOnClassesThat()
.resideInAnyPackage("org.springframework..")
.check();
^ Tipo de regla ^ Qué controla ^
| Dependencias entre paquetes | Límites de capa |
| Ciclos | Que el grafo de dependencias sea acíclico |
| Convenciones de nombres | Dónde vive cada tipo de clase |
===== 4. Conexión con los diagramas =====
Las reglas de ArchUnit son la versión ejecutable de lo que se dibuja:
* El **diagrama de clases** documenta las dependencias diseñadas.
* El diagrama **C4 de componentes** muestra los módulos permitidos.
* **ArchUnit** comprueba que el código real se ajusta a ambos.
Las reglas de arquitectura no se negocian prueba a prueba: se acuerdan y se automatizan. Si una regla debe cambiar, se cambia la regla deliberadamente, no rompiéndola en silencio.