Files
java21-template/tmpl-api/src/test/java/com/example/tmpl/architecture/ArchitectureTest.java
T
Bruno GaoandQwen-Coder eefe2c5b34 M2: Add complete Java21 template with Clean Architecture implementation
- Add four-layer modular structure (domain, application, infrastructure, api)
- Implement complete CRUD example with Order entity
- Add ArchUnit architecture compliance tests
- Add Maven configuration with proper dependency management
- Add Dockerfile for containerization
- Add Kustomize deployment templates
- Add initialization script (init.sh)
- Add CI/CD workflow configuration
- Add comprehensive documentation and configuration files

Co-authored-by: Qwen-Coder <[email protected]>
2026-02-11 23:54:21 +08:00

140 lines
5.2 KiB
Java

package com.example.tmpl.architecture;
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 org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.*;
import static com.tngtech.archunit.library.Architectures.layeredArchitecture;
/**
* 【架构测试】架构合规性测试。
*
* <p>验证分层架构、依赖方向、注解使用等是否符合架构规范。
*/
class ArchitectureTest {
private final JavaClasses importedClasses = new ClassFileImporter().importPackages("com.example.tmpl");
@Test
void domainLayerShouldNotDependOnOtherLayers() {
ArchRule rule = classes()
.that().resideInAPackage("..domain..")
.should().onlyDependOnClassesThat().resideInAnyPackage(
"java..",
"jakarta.validation..",
"com.example.tmpl.domain.."
);
rule.check(importedClasses);
}
@Test
void applicationLayerShouldOnlyDependOnDomain() {
ArchRule rule = classes()
.that().resideInAPackage("..application..")
.should().onlyDependOnClassesThat().resideInAnyPackage(
"java..",
"org.springframework.transaction..",
"org.springframework.context..",
"org.springframework.data.commons..",
"com.example.tmpl.domain..",
"com.example.tmpl.application.."
);
rule.check(importedClasses);
}
@Test
void infrastructureLayerShouldOnlyDependOnDomainAndApplication() {
ArchRule rule = classes()
.that().resideInAPackage("..infrastructure..")
.should().onlyDependOnClassesThat().resideInAnyPackage(
"java..",
"org.springframework..",
"org.postgresql..",
"org.flywaydb..",
"org.mapstruct..",
"com.example.tmpl.domain..",
"com.example.tmpl.application..",
"com.example.tmpl.infrastructure.."
);
rule.check(importedClasses);
}
@Test
void apiLayerShouldOnlyDependOnApplicationAndInfrastructure() {
ArchRule rule = classes()
.that().resideInAPackage("..api..")
.should().onlyDependOnClassesThat().resideInAnyPackage(
"java..",
"org.springframework..",
"org.springdoc..",
"io.micrometer..",
"jakarta.validation..",
"com.example.tmpl.domain..",
"com.example.tmpl.application..",
"com.example.tmpl.infrastructure..",
"com.example.tmpl.api.."
);
rule.check(importedClasses);
}
@Test
void controllersShouldNotDependOnInfrastructureDirectly() {
ArchRule rule = classes()
.that().resideInAPackage("..api.controller..")
.should().onlyDependOnClassesThat().resideInAnyPackage(
"java..",
"org.springframework.web..",
"org.springframework.http..",
"jakarta.validation..",
"com.example.tmpl.domain..",
"com.example.tmpl.application..",
"com.example.tmpl.api.dto..",
"com.example.tmpl.api.mapper..",
"com.example.tmpl.api.."
);
rule.check(importedClasses);
}
@Test
void transactionalAnnotationShouldOnlyBeInApplicationLayer() {
ArchRule rule = noClasses()
.that().resideOutsideOfPackage("..application..")
.should().beAnnotatedWith(Transactional.class);
rule.check(importedClasses);
}
@Test
void domainClassesShouldNotUseSpringAnnotations() {
ArchRule rule = noClasses()
.that().resideInAPackage("..domain..")
.should().beAnnotatedWith(Service.class);
rule.check(importedClasses);
}
@Test
void layeredArchitectureTest() {
ArchRule rule = layeredArchitecture()
.layer("Domain").definedBy("..domain..")
.layer("Application").definedBy("..application..")
.layer("Infrastructure").definedBy("..infrastructure..")
.layer("API").definedBy("..api..")
.whereLayer("Application").mayNotBeAccessedByAnyLayer()
.whereLayer("Domain").mayOnlyBeAccessedByLayers("Application", "Infrastructure")
.whereLayer("Infrastructure").mayOnlyBeAccessedByLayers("API", "Application")
.whereLayer("API").mayNotBeAccessedByAnyLayer();
rule.check(importedClasses);
}
}