@Service类是否应该与@Controller类具有相同的接口?

问题描述

我们是一个小团队,开始一个新项目,并使用Spring-Boot框架开发后端。

后端项目将具有Spring-Boot应用程序的以下标准层:

  • 控制器(映射端点和句柄)
  • 服务(处理业务逻辑)
  • 存储库(用于抽象数据库并与数据库进行交互)

我们还有一个实体包,所有代表数据库实体的类都位于其中。 我们还使用一个库来映射具有Api模型的实体。

这并没有持续很长时间,我们在团队内的第一次辩论就发生了。所以我认为在外面征求意见是个好主意。

我的其他合作伙伴认为,控制器应包含尽可能少的代码行。 理想情况下,只包含一行代码,分别调用相应的Service,其余代码在Service中处理。

反对这种方法,因为如果我们这样做,那么服务和控制器最终将具有相同的接口,这是错误的。

但是,我同意以下原则:控制器应具有尽可能少的代码行。 我认为控制器方法应该只接受并返回Api模型,并且控制器中不应该存在实体的概念。

同时,我认为services方法应该接受并返回实体,而不是与ApiModels一起使用。

我们应该在Controller和Service之间引入一个新层吗?是否存在与此主题相关的一些我不知道的标准?

我将通过两种方法(我和我的合作伙伴的方法)为您提供具体示例:

第一种方法

public class UserController implements UsersApi {

  @Autowired
  private UserService userService;

  @Override
  public ResponseEntity<UserAPIModel> createuser(@Valid UserAPIModel body) {
    User createdUser = userService.save(UserMapper.INSTANCE.toUserEntity(body));
    return ResponseEntity.ok(UserMapper.INSTANCE.toUserAPIModel(createdUser));
  }
}

服务:

public class UserService {

  @Autowired
  private UserRepository repository;

  public User save(User entity) {
    return repository.save(entity);
  }

}

第二种方法

public class UserController implements UsersApi {

  @Autowired
  private UserService userService;

  @Override
  public ResponseEntity<UserAPIModel> createuser(@Valid UserAPIModel body) {
    return ResponseEntity.ok(userService.createuser(body));
  }
}

和服务:

public class UserService {

  @Autowired
  private UserRepository repository;

  public UserAPIModel createuser(UserAPIModel body) {
    User user = repository.save(UserMapper.INSTANCE.toUserEntity(body));
    return UserMapper.INSTANCE.toUserAPIModel(user);
  }

}

正如您在第一种方法中看到的那样,从实体到Api模型的映射,反之亦然,是在Controller中完成的。在第二种方法中,此映射是在服务中完成的。

您会推荐哪个?还是您认为最好在Controller和Service之间引入一个新层?

对这个简单的示例做出决定,可以帮助我们创建更通用的规则,并为以后的类似问题设定标准。

解决方法

首先,您的问题是very broad,该问题在一个帖子中包含了许多问题,您不应该在此站点上这样做。将来,请尝试提出更具针对性的问题。

TL; DR:

  1. @Controller是一个 Web层,即负责处理HTTP请求-响应责任的层,您的Java代码应在语义上谈论 web ,即您的控制器的责任应该是接受HTTP请求,并以HTTP响应进行响应。不管您是否在两者之间做一些逻辑,都不应完全由控制器负责,而应该站在其依赖项的肩膀上(大多数情况下为@Service)。

  2. @Service是一个业务层,即负责处理与业务相关的任务的层,其中所有主要业务逻辑,事务,文件处理,批处理或等等。

您可以混合搭配这两个,但这不是一个好主意。而是遵循明确的separation of concerns模式,其中@Controller负责Web层,@ Service负责业务逻辑。


您的积分:

我的其他合作伙伴认为控制器应该包含尽可能少的代码行

不一定,这取决于。根据给定的情况,控制器可能会做一些工作,并且没有 controller-specific Clean Code惯例。 Clean Code会指示您,您的方法通常不要太长且凌乱(有一个有趣的规则,当水平放置时,任何方法都不能超过您的五个手指的高度),但同样,这是非常通用且值得商rule的规则;

理想情况下只包含一行代码

通常不要遵循此规则。这很少是真的,除非您的控制器除了调用某些服务API(例如return findAll();)以外什么都不做;

调用相应服务的位置,其余部分在服务中处理

您的合伙人是正确的,如果他/她的意思是与业务相关的(即交易,数据库访问器,批处理或任何其他类型的过程),不在网络层职责范围之内),最好在服务层中处理;

@Service组件面向to be acting as 商业服务立面;

我反对这种方法,因为如果我们这样做,那么服务和控制器最终将具有相同的接口,这是错误的。

不,他们不会。 @Controller不需要任何Java接口,按照这个词组的经典含义(例如public interface Foo{..}),它是Spring托管的组件,并且您不需要注入其实现,而在我们谈论这种情况时,情况并非如此关于@Service组件;

但是,我同意以下原则:控制器应具有尽可能少的代码行。

不要在 controller 上对此进行概念化。通常,方法应该尽可能清晰,并且如果控制器有点长也没什么错。这与控制器无关,因此,这与方法的设计有关,是否可以提取/提取某些东西,以及使您的代码更整洁。但是,请尽可能使用YAGNI principle,不要急于就控制器的代码行进行过早的优化。不要走太久..但不要在计算行数时困扰自己;

我认为控制器方法应仅接受并返回API模型,并且控制器中不应包含实体的概念。

错了。 @Controller的责任并不直接绑定到API模型,它的责任是接受HTTP消息,对其进行一些处理(在@ Service,UtilityClass等的帮助下)并通过HTTP响应进行响应。在某些情况下,您可以使控制器直接依赖于数据访问层,但又要依赖于它。

是否接受模型,这也很大程度上取决于您,您可以看到我的other answer解释是否应该使用DTO;

我们应该在Controller和Service之间引入新的一层吗?

否,除非有必要。控制器应该是您的请求结束的第一层;它通常应该依赖于Service,而Service最终将依赖于Data Persistence层(Spring Data,JDBC,Integrations或任何其他东西)。

,

伙计们!
几个月前,我也开始从事Springboot的工作,我可以告诉您,从个人经验来看,最好在Controller和Service之间创建一个Mapper以便更好地进行。通常,尤其是在此类项目中,必须使多样化,而不是让Controller或Service在其使用范围之外执行任务。在这种情况下,控制器仅需要启动命令,也不应该与Repo进行任何直接连接。从经验中我告诉你,在较大的项目中,这种心态会有所收获

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...