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.

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.

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

// 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

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.
  • clase/daw/ed/2eval/arquitectura.txt
  • Última modificación: 2026/10/12 12:45
  • por cesguiro