【问题标题】:Should @Service classes have the same interface as @Controller classes?@Service 类是否应该与@Controller 类具有相同的接口?
【发布时间】:2020-10-16 13:15:31
【问题描述】:

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

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

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

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

这并没有持续多久,我们在团队中进行了第一次辩论。所以我认为在外面寻求建议是个好主意。

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

我反对这种做法,因为如果我们这样做,Service 和 Controller 最终将拥有相同的界面,这感觉不对。

但是,我同意控制器应具有尽可能少的代码行的原则。 我认为控制器方法应该只接受和返回 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 之间引入一个新层更好?

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

【问题讨论】:

  • 我认为对于简单的 CRUD 案例,除非您在持久化之前要运行一些业务逻辑,否则服务类会过度。
  • @code_mechanic 是的,会有业务逻辑,我们需要服务层。为了简单起见,我举了一个简单的例子。
  • 我喜欢将编排/映射工作分离到一个单独的层(在域层之上),并使我的控​​制器尽可能虚拟。看看 DDD 概念“域服务”和“应用程序服务”。
  • 当它们不同时使用单独的域实体和 API 模型(否则它会引入一个无用的层来维护)。那么问题就变成了,应该有不同的域实体和 API 模型吗?端点响应是否公开数据库表结构?例如,假设一个表被规范化,它改变了一些域实体。端点的消费者是否需要改变?

标签: java spring-boot object-oriented-analysis


【解决方案1】:

首先,你的问题是very broad,它在一篇文章中包含了许多问题,你不应该在这个网站上这样做。将来,请尝试提出更有针对性的问题。

TL;DR:

  1. @Controller 是一个 Web 层,即负责处理 HTTP 请求-响应责任的层,您的 Java 代码应该在语义上与 web 对话,即您的责任控制器应该接受 HTTP 请求,并以 HTTP 响应响应。无论你是否在两者之间做一些逻辑,不应该是控制器的纯粹责任,它应该站在其依赖的肩膀上(@Service,在大多数情况下)。

  2. @Service 是一个业务层,即负责处理业务相关任务的层,所有主要的业务逻辑、事务、文件处理、批处理等都可能在此执行地点。

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


您的积分:

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

不一定,视情况而定。控制器可能会做一些工作,具体取决于给定的情况,并且没有控制器特定清洁代码实践。 Clean Code 会告诉您,您的方法 通常不应该太长和太乱(有一条有趣的规则,当水平放置时,任何方法都不应超过 5 根手指的高度),但同样,这是非常通用且值得商榷的规则;

最好只包含一行代码

请不要遵循此规则。这很少是真的,除非你的控制器除了调用一些服务 API(比如return findAll();)什么都不做;

调用各个Service的地方,其余的在Service中处理

您的合作伙伴是正确,如果他/她的意思是,业务相关(即事务性、数据库访问器、批处理或任何其他类型的流程,即不在 Web 层的职责范围内)最好在服务层中处理;

@服务组件面向to be acting as 业务服务门面;

我反对这种做法,因为如果我们这样做,Service 和 Controller 最终将拥有相同的界面,这感觉不对。

不,他们不会。 @Controller不需要任何Java接口,在这个短语的经典意义上(例如public interface Foo{..}),它是一个Spring托管组件,你不需要注入它的实现,当我们谈论@时情况并非如此服务组件;

但是,我同意控制器应该尽可能少的代码行的原则。

不要在控制器上对此进行概念化。方法,一般来说,应该尽可能的清晰,如果控制器有点长也没什么问题。这与控制器无关,因此,它与你的方法的设计有关,你是否可以提取/抽象一些东西让你的代码更干净。但是,尽可能应用YAGNI principle,不要急于对控制器的代码行进行过早优化。不要走太久......但不要在这里为数线费心;

我认为控制器方法应该只接受和返回 API 模型,并且控制器中不应该存在实体的概念。

错了。 @Controller 的职责不直接绑定到 API 模型,它的职责是接受 HTTP 消息,用它做一些事情(在@Service、UtilityClass 等的帮助下)并用 HTTP 响应响应。在某些情况下,您可以使您的控制器直接依赖于数据访问层,但同样 - 这取决于。

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

我们是否应该在 Controller 和 Service 之间引入一个新层?

不,除非有必要。控制器应该是您的请求结束的第一层;它通常应该依赖于服务,而服务最终将依赖于数据持久层(Spring Data、JDBC、集成或任何其他东西)。

【讨论】:

    【解决方案2】:

    大家好!
    几个月前我也开始研究 Springboot,我可以告诉你,从个人经验来看,最好在 Controller 和 Service 之间创建一个 Mapper 以便更好地进行。一般来说,特别是在这类项目中,必须多样化,而不是让 Controller 或 Service 执行超出其用途的任务。在这种情况下,控制器只需要启动一个命令,也不应该有任何与 Repo 的直接连接。根据经验,我告诉你,在大型项目中,这种心态会得到回报

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-20
      • 2013-03-28
      • 2023-03-19
      • 1970-01-01
      相关资源
      最近更新 更多