Spring三层架构中业务逻辑归属及异常处理最佳实践咨询
关于Spring三层架构的业务逻辑分层与异常处理最佳实践
嘿,刚接触Spring三层架构有这些疑问太正常了,我结合实际开发经验给你梳理清楚:
1. 业务逻辑应该放在哪一层?
答案是Service层,这是三层架构里专门负责封装核心业务规则的地方。
三层架构的职责划分要清晰:
- Controller(控制层):仅作为Web请求的入口和出口,负责接收前端请求参数、做简单的参数校验、将请求数据转换为Service需要的格式(比如DTO转Entity)、调用Service方法、最后封装响应结果返回给前端。绝对不要在这里写业务逻辑,比如判断用户权限、计算订单金额这类操作都不属于它的职责。
- Service(业务层):承载所有核心业务逻辑,比如用户注册时的用户名唯一性校验、密码加密、订单生成时的价格计算、库存扣减等。它会调用Repository层完成数据的持久化操作,同时处理业务规则的判断。
- Repository(数据访问层):只负责和数据库交互,做最基础的CRUD操作,不包含任何业务逻辑,比如根据ID查用户、保存用户、更新库存等。
2. "controller仅需调用service,业务逻辑需全部置于service层"的说法是否正确?
这个说法90%以上的场景是正确的,但要注意Controller可以做一些请求层面的非业务校验,比如检查请求参数是否为空、格式是否合法(比如手机号格式),这些属于Web层的职责,不属于业务逻辑。
举个代码示例:
// Controller层,只做请求处理和参数校验 @PostMapping("/register") public ResponseEntity<String> register(@Valid @RequestBody UserDTO userDTO) { userService.register(userDTO); return ResponseEntity.ok("注册成功"); } // Service层,处理核心业务逻辑 public void register(UserDTO userDTO) { // 业务逻辑:校验用户名是否已存在 if (userRepository.existsByUsername(userDTO.getUsername())) { throw new UsernameDuplicateException("用户名已存在"); } // 业务逻辑:加密密码 String encryptedPassword = passwordEncoder.encode(userDTO.getPassword()); // 转换为Entity并保存 User user = new User(userDTO.getUsername(), encryptedPassword); userRepository.save(user); }
3. Service层的异常需要处理吗?
当然需要处理,但要分情况对待:
- 业务异常:比如用户名重复、余额不足、权限不够这类符合业务规则的异常,建议自定义专属的业务异常类(比如
UsernameDuplicateException、InsufficientBalanceException),在Service层抛出这些异常,然后在Controller层通过@ExceptionHandler或者全局异常处理器统一捕获,转换成友好的HTTP响应返回给前端(比如返回错误码和错误信息)。 - 系统异常:比如数据库连接失败、空指针异常、SQL语法错误这类底层异常,Service层可以选择捕获后包装成业务异常抛出,或者直接让异常往上抛,交给Spring的全局异常处理器处理,绝对不要把底层的系统异常直接返回给前端,避免暴露系统细节。
一些额外的最佳实践
- 事务管理放在Service层:业务逻辑往往涉及多个数据库操作(比如转账时的扣钱和加钱),在Service方法上添加
@Transactional注解来保证事务的原子性,避免数据不一致。 - 避免跨层调用:Controller不要直接调用Repository,Service也不要处理Web层的请求响应,严格遵守分层调用规则(Controller -> Service -> Repository)。
- DTO与Entity分离:Controller接收和返回用DTO(数据传输对象),Service和Repository用Entity(实体类),避免直接暴露数据库实体给前端,同时也方便扩展。
- 单元测试重点覆盖Service层:因为Service层包含核心业务逻辑,是测试的重点,Controller层可以侧重集成测试,验证请求响应的正确性。
内容的提问来源于stack exchange,提问作者Ricki
相关产品推荐
相关产品推荐

