You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot三层架构最佳实践:分层代码写法是否正确?

现有写法不符合三层架构最佳实践

你当前的代码主要存在3个核心问题:

  • 职责越界严重:ResponseEntity、HttpStatus是Spring Web层专属的传输层对象,Service作为业务逻辑层,不应该感知任何和HTTP协议、Web交互相关的逻辑。如果在Service里直接返回Web层对象,后续你要复用Service逻辑做定时任务、RPC接口、离线任务的时候,会强依赖Spring Web包,根本无法独立复用。
  • 异常处理完全冗余失效:两层都写了大而全的try-catch捕获通用Exception,但Service层已经把所有异常都捕获、封装成ResponseEntity返回了,根本不会有异常抛到Controller层,你写在Controller里的catch块是永远不会触发的死代码。另外这种吞异常的写法会丢失异常栈信息,后续排查数据库连接失败、SQL错误等问题的时候根本找不到根因。
  • 逻辑耦合错位:判断列表为空返回204状态码、组装HTTP响应体这类属于请求处理的逻辑,本来就是Controller层的职责,硬塞到Service层只会让业务层逻辑变混乱。另外你Controller里的请求处理方法用了private修饰,这是Spring MVC的写法错误,请求映射方法必须是public,否则框架无法正常代理调用。
各层职责边界与正确写法

三层架构的核心是每层只做自己职责内的事,不越界、不冗余,各层的明确分工如下:

  • Repository层:只做纯数据访问,封装所有数据库操作,直接返回领域实体/集合,不做业务判断、不捕获异常,数据库抛出的异常直接向上抛出即可。
  • Service层:只处理纯业务逻辑,包括业务规则校验、流程编排、事务控制、调用Repository获取/存储数据,遇到业务错误直接抛自定义业务异常即可,返回值就是纯业务对象(比如List<Customer>、单个Customer实体),绝对不要引入Web层相关的类。
    Service层参考写法:
    // 注意返回值是纯业务集合,不涉及任何Web相关对象
    public List<Customer> getAllCustomers() {
        List<Customer> customers = new ArrayList<>();
        customerRepository.findAll().forEach(customers::add);
        // 这里只保留业务相关逻辑,比如数据权限过滤、业务规则校验
        // 如果业务规则定义"客户列表为空属于业务异常",直接抛自定义业务异常,不要在这里拼HTTP响应
        return customers;
    }
    
  • Controller层:只负责接收请求参数、做基础参数校验、调用Service拿到业务结果、组装HTTP响应(设置对应状态码、返回结构)。不要在每个接口方法里单独写try-catch,推荐用Spring提供的@RestControllerAdvice做全局异常处理,统一拦截所有层抛出的异常,封装成统一格式的错误响应,避免重复代码。
    Controller层参考写法:
    @GetMapping("/viewList")
    public ResponseEntity<List<Customer>> getAllCustomers() {
        List<Customer> customers = customerService.getAllCustomers();
        if (customers.isEmpty()) {
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        }
        return ResponseEntity.ok(customers);
    }
    
    全局异常处理器参考写法:
    @RestControllerAdvice
    public class GlobalExceptionHandler {
        // 捕获所有未处理的服务端异常
        @ExceptionHandler(Exception.class)
        public ResponseEntity<String> handleServerException(Exception e) {
            // 生产环境这里要先打印错误日志留痕,再返回对用户友好的提示
            return new ResponseEntity<>("服务内部异常,请稍后重试", HttpStatus.INTERNAL_SERVER_ERROR);
        }
    
        // 自定义业务异常可以单独写对应处理逻辑,返回对应状态码
        @ExceptionHandler(CustomerNotFoundException.class)
        public ResponseEntity<String> handleCustomerNotFound(CustomerNotFoundException e) {
            return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
        }
    }
    
常见误区说明
  • 不需要把Service层的所有额外逻辑都删掉:Service层要保留的是和业务强相关的处理逻辑,比如数据权限过滤、事务控制、业务规则校验、多步骤业务流程编排这类和Web传输无关的逻辑,只需要把HTTP状态码设置、响应结构封装这类Web专属逻辑移到Controller层即可。
  • 不要每层都做异常捕获:异常应该在最适合处理的层统一收口,不要在中间层随意吞异常。数据层异常、业务异常都可以直接向上抛,最终到全局异常处理器里统一封装响应,既减少重复代码,也能保留完整异常栈方便排查问题。

内容的提问来源于stack exchange,提问作者renad sahal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 22:01:11