- 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]>
140 lines
5.2 KiB
Java
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);
|
|
}
|
|
} |