【问题标题】:Why do I need a repository and a service + contract为什么我需要一个存储库和一个服务+合同
【发布时间】:2017-03-12 12:01:11
【问题描述】:

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

在这些框架中,这就是我所拥有的:

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 教程之后,我现在有了这个:

模型(实体/道?):仅包含属性,除了 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 guides on Repositories 上阅读的内容,您已经获得了默认的 CRUD 方法 save, read, update, delete.. ,除此之外,您还可以使用查询方法进行 findUserBy... 或编写带注释的原始查询。所以我有点困惑,为什么不声明 UserServiceContractUserService 只是为了实现存储库已经提供的功能?

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

【问题讨论】:

  • 您并不严格需要该服务。您可以直接使用 DAO/Repository。如果您想将低级数据库表示转换为严格的内部模型(不可变和断言),该服务可以派上用场。这些模型更适合由更高级别的层处理(因此您不会暴露 ID 等数据库内部结构)。如果您需要这种额外的分层,这取决于您的应用程序有多大。

标签: java spring spring-data kotlin dao


【解决方案1】:

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

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

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

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

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

是否使用接口来定义服务合同取决于您。 Spring 不会强迫你这样做。你也可以混合使用服务和存储库,如果你想在脚上开枪,但 Spring 不鼓励这样做,如果你使用 spring-data-jpa,你将无法做到这一点,它基本上会生成一个基于接口为您实现存储库。

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

【讨论】:

  • 哦,哇,谢谢,这实际上是一个很好的解释。因此,例如在我的情况下,如果您查看第一个 sn-p,我有 3 个函数 getUsersScore,这是一种基于 3 个字段计算用户分数的算法,这些字段都是用户实体的一部分。所以这个方法会留在这里。但另外两个:assignUserToDevice() 接受设备实体并将用户分配给它,connectUserToService 接受例如:MessagingService 类。这两种方法更适合 UserService。
  • Tho than 在UserService 中重新实现 CRUD 方法也有意义吗?由于这种方式,我避免在代码的其他部分同时使用UserRepositoryUserService。它还使测试更容易。在任何地方都使用UserService
猜你喜欢
  • 2016-10-25
  • 1970-01-01
  • 1970-01-01
  • 2021-03-09
  • 1970-01-01
  • 1970-01-01
  • 2011-08-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多