繁体   English   中英

Spring 引导:执行最佳实践

[英]Spring boot: enforce best practices

我正在建立一个项目并想知道是否有任何方法可以仅从服务层强制执行存储库实例访问?

我们可以创建测试来实现需求。 我在其中一个项目中创建了相同的项目,该项目验证服务和存储库不应依赖于 web 层 您可以根据需要修改包。

import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;

import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.core.importer.ImportOption;
import org.junit.jupiter.api.Test;

class ArchTest {

    @Test
    void servicesAndRepositoriesShouldNotDependOnWebLayer() {
        JavaClasses importedClasses = new ClassFileImporter()
            .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
            .importPackages("com.learning.springboot");

        noClasses()
            .that()
            .resideInAnyPackage("com.learning.springboot.service..")
            .or()
            .resideInAnyPackage("com.learning.springboot.repository..")
            .should()
            .dependOnClassesThat()
            .resideInAnyPackage("..com.learning.springboot.web..")
            .because("Services and repositories should not depend on web layer")
            .check(importedClasses);
    }
}

如果在定义存储库和服务时不使用 public 关键字 class 并将它们放在一个 package 中。 当然,您需要从 Controller 与 Service 进行通信。 最好的方法是创建公共的接口(在同一个包中),让我们说:

public interface UserFacade

这是由(相同的 package,没有 public 关键字)实现的:

@Component
class UserFacadeImpl implements UserFacade {

   private YourService service;

   void someMethod() {
       service.doSomethingWithRepository();
   }
}

然后,您可以在此外观中定义使用您的服务或在此 package 中定义的任何其他服务的方法。 最后,从 package 外部访问您的存储库的唯一方法是通过 UserFacade 中定义的方法间接访问,因此只有同一 package 中的服务可以直接使用此存储库。

这是 IMO 一种有趣的方法,尽管(免责声明)我还没有看到有人真正实现了这一点:

创建以下 maven 模块:

  1. 控制器
  2. 服务-api
  3. 服务实现
  4. 回购API
  5. Repos-impl
  6. 弹簧启动应用程序

除了最后一个之外的所有模块都是“普通”的 jars,最后一个是 JAR/WAR 构建,使用 spring 引导 maven 插件。

现在,定义依赖项如下:

  1. Controller 依赖于 services-api
  2. services-api 只包含服务的接口,没有依赖
  3. services-impl 依赖于 services-api 和 repos-api
  4. Repos-Api 再次只是接口,没有依赖项
  5. Repos-Impl 依赖于 Repos-api 和持久性驱动程序(如 JDBC 驱动程序或任何您使用的驱动程序)
  6. Spring-boot-application 包含对 Controllers 模块、Service-impl(传递 Service-Api)和 Repos-impl(传递 repo api)的依赖项。 这是一种在运行时将所有内容粘合在一起的包装器。

所以现在,如果您正在编写 controller(模块控制器),那么您只能注入服务的接口,否则将无法编译。 您不能注入存储库(既不是接口,也不是实现),它也不会编译。

当您创建服务(接口)时 - 您不需要任何依赖关系,服务之间的依赖关系将在可能依赖于其他服务的实现级别提供。

当您编写服务的实现时 - 可以注入存储库 api,但不是实现(同样,它不会编译)。

如果您使用的是 Java 9+,您可能会使用我不熟悉的模块系统创建相同级别的分离,但核心思想很简单:

Spring 是一个运行时框架,我的回答是尝试在编译级别“捕捉”这些“不良”用法。

我知道提出的分离“太严格”,可能很多团队都不会费心去做这样的事情,我自己相当“依赖”从事该项目的程序员,他们“知道自己在做什么”,但我也明白有时你不能真正依赖它,因此我的回答:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM