演示:React + Spring + ArchUnit的开源项目


使用 ArchUnit 强化Java架构:展示如何使用 Spring 运行前端 (React) 和后端 (Java) 的 Java开源项目,创建具有前端和后端的项目,以便在本地或通过浏览器轻松运行。

前端技术:

  • JavaScript
  • React 17.0.2
  • MUI 5
  • Jest
  • GitHub actions

后端Java:

  • Java 11
  • SpringBoot 2.6.3
  • Spring
  • Hibernate
  • Swagger - OpenAPI 1.6.7
  • MapStruct
  • GitHub actions
  • Gradle — 7.4
  • ArchUnit — 0.23.1
  • JUnit 5–5.8.2

这是一个单体分层架构:

  • 1st → 项目层
  • 2nd → 单元测试,我们对更好架构的守护者
  • 3rd → Gradle 构建文件
  • 4th → 对archUnit的依赖

UT(单元测试)
只需一个 UT,您就可以设置您的监护人来保证系统架构。
在我们的设置中,首先我们需要通知 UT 不考虑测试包。如果您的情况有任何例外,请也添加到设置中。
然后是一个愚蠢的测试,说任何 web 包都可以被 web 包内的任何类访问。
最后,更重要的是关于层,我们控制层,非常出色:

package com.costa.luiz.zero2hero;

import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.core.importer.ImportOption;
import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.library.Architectures;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
import static com.tngtech.archunit.library.Architectures.layeredArchitecture;

class ArchitectureGuardianTest {

    private static JavaClasses importedClasses;

    @BeforeAll
    static void setUp() {
        ImportOption ignoreTests = location -> {
            return !location.contains("/test/"); // ignore any URI to sources that contains '/test/'
        };
        importedClasses = new ClassFileImporter().withImportOption(ignoreTests).importPackages(
"com.costa.luiz.zero2hero");
    }

    @Test
    void webShouldOnlyBeAccessedByWeb() {
        ArchRule serviceRule = classes()
                .that().resideInAPackage(
"..web..")
                .should().onlyBeAccessed()
                .byAnyPackage(
"..web..");
        serviceRule.check(importedClasses);
    }

    @Test
    void layeredArchitectureGuardian() {
        Architectures.LayeredArchitecture layeredArchitecture = layeredArchitecture()
                .layer(
"Controller").definedBy("..web..")
                .layer(
"Business").definedBy("..business..")
                .layer(
"Persistence").definedBy("..persistence..")
                .whereLayer(
"Controller").mayNotBeAccessedByAnyLayer()
                .whereLayer(
"Business").mayOnlyBeAccessedByLayers("Controller", "Business")
                .whereLayer(
"Persistence").mayOnlyBeAccessedByLayers("Business");

        layeredArchitecture.check(importedClasses);
    }
}