为什么我需要一个存储库和一个服务+合同

Why do I need a repository and a service + contract

我是 Spring 的新手,来自 PHP/Larvel、Ruby/Rails 和 Python/Django。来自这些框架,我习惯只看到模型(Entity/Dao?),其他一切都由 Framework/ORM/QueryBuilders 处理,因此我要做的就是声明一个模型并向其中添加任何operations/relations/methods我需要。

在这些框架中,这就是我想要的:

class User extends BaseModel
{
  public function devices() { .. } // Relation to Device model

  // Any additional functions on user go here these can operate on 
  // just the User model or pull additional data from DB or other models.  
  public function assignUserToDevice();
  public function getUsersScore();
  public function connectUserToService();
}

但是按照这个 Spring 教程,我现在有了这个:

模型(Entity/Dao ?):只包含属性,除了 GET/SET 和关系之外没有方法。

@Entity  
@Table(name = "users")  
class User {
  @Id
  @GeneratedValue  
  var id: Long = 0

  var username: String = "" //...
}

存储库:

interface UserRepository : CrudRepository<User, Long> {
  fun findByUsername(username: String): User
}

用户服务合同:

interface UserServiceContract {

    /**
     * Attempts to find User associated with the given id.
     * @throws ModelNotFoundException if not user has been found associated with the given id.
     * @param id given users id.
     * @return User associated with the given id.
     */
    @Throws(ModelNotFoundException::class)
    fun find(id: Long): User

    /**
     * Returns all existing users.
     * @return list of all existing users.
     */
    fun findAll(): List<User>
}  

还有一项服务:

@Service
class UserService : UserServiceContract {

    @Autowired
    lateinit var userRepository: UserRepository

    /**
     * Attempts to find User associated with the given id.
     * @throws ModelNotFoundException if not user has been found associated with the given id.
     * @param id given users id.
     * @return User associated with the given id.
     */
    override fun find(id: Long) = userRepository.findOne(id) ?: throw ModelNotFoundException("User not found!")

    override fun findAll(): List<User> {
        throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
}  

问题:

  1. 为什么我需要 UserRepositoryUserService + UserServiceContract?根据我阅读的 Spring 存储库指南,您已经获得了默认的 CRUD 方法 save, read, update, delete.. 除此之外,您还有查询方法,您可以在其中执行 findUserBy... 或编写注释原始查询。所以我有点困惑为什么不声明 UserServiceContractUserService 只是为了实现存储库已经提供的功能?

  2. 以前我会在所述模型中保留工作 on/with 我的模型的方法,但是正如我从很少的资源中注意到的那样,这不是它们在 Spring 中的位置,所以我应该在哪里保留它们 ?这就是为什么有 UserServiceUserServiceContract 的原因吗? 我真的应该只将 UserRepository 用于数据库访问,并在 UserServiceContract 中声明工作方法 on/with 用户喜欢:public function connectUserToService() ?

用户是实体。它基本上表示数据库中 table 中的一行。它绝对可以有方法。将与该实体无关的方法仅放在实体 class 中通常不是一个好主意。尤其是需要访问外部依赖项(如存储库或服务)的方法。

存储库是允许执行与给定实体相关的查询或保存该实体的新实例的组件。

服务是包含业务逻辑的。它可以像简单地委托给存储库一样简单,但除非您的应用程序只是查看数据库中的信息,否则它通常包含更复杂的逻辑,涉及多个实体和存储库。

将存储库与服务分开很有用,因为它允许

  • 避免混合责任,
  • 轻松测试查询,而不必使用它们调用复杂的逻辑
  • 通过模拟存储库轻松高效地测试业务逻辑
  • 划分事务边界:服务方法通常是一个事务。

是否使用接口来定义服务契约取决于您。 Spring 不会强迫你那样做。如果你想搬起石头砸自己的脚,你也可以混合服务和存储库,但 Spring 不鼓励这样做,如果你使用 spring- data-jpa,它基本上基于接口为您生成存储库实现。

我不太了解您习惯使用的框架,但是在考虑 spring 应用程序的设计时需要牢记这一点:Spring 是一个依赖项注入框架,因此包括将组件注入其他组件。实体不是 Spring 组件,因此如果它们包含业务逻辑(这又会混合职责,IMO),则不能注入它们所需的依赖项。而依赖注入本身主要对制作代码有用 testable,并能够围绕方法调用添加方面(例如,启动和提交事务)。这就是驱动 Spring 设计的原因:单一职责原则、依赖注入的可测试性,以及 .