区分于三层架构的四层架构(Java 后端分层设计的完整指南)
四层架构Java 后端分层设计的完整指南适用场景Spring Boot / Spring MVC 等 Java Web 后端关键词Controller · Service · Repository · Entity · 分层架构 · 职责分离我遇到的问题刚学 Java Web 开发时很容易把所有逻辑堆在一个类里查数据库、算业务、拼 JSON 全写在一起。小 demo 能跑项目一变大就改不动、测不了、协作也痛苦。后面又学习了三层架构(Controller → Service → Mapper → DB),但是在高复杂的业务代码中,整个项目的结构又变得很复杂了,然后便有了四层架构,可以说这是后端分层的最佳实现了.**四层架构(Controller → Service → Repository → Mapper → Entity)**是后端里最经典、也最实用的组织方式之一。它把代码按职责切成四块让每一层只做一件事。这篇文章从概念、职责、调用关系到常见误区系统讲清楚「四层」到底是什么、为什么要这么分、日常开发怎么落地。一、四层架构是什么四层架构Four-Layer Architecture在 Java 后端语境下通常指层级英文名核心职责控制层Controller接收 HTTP 请求返回响应业务层Service实现业务规则与流程编排数据访问层Repository / DAO读写数据库屏蔽持久化细节实体层Entity / Model映射数据库表或领域对象┌─────────────────────────────────────────────────────────┐ │ HTTP 请求 / 响应 │ └───────────────────────────┬─────────────────────────────┘ ▼ ┌─────────────────────────────────────────────────────────┐ │ Controller 层 参数校验 · 路由 · 统一响应格式 │ └───────────────────────────┬─────────────────────────────┘ ▼ ┌─────────────────────────────────────────────────────────┐ │ Service 层 业务逻辑 · 事务 · 权限判断 · 流程编排 │ └───────────────────────────┬─────────────────────────────┘ ▼ ┌─────────────────────────────────────────────────────────┐ │ Repository 层 CRUD · 复杂查询 · 缓存读写 │ └───────────────────────────┬─────────────────────────────┘ ▼ ┌─────────────────────────────────────────────────────────┐ │ Entity 层 与表结构对应的 Java 对象 │ └───────────────────────────┬─────────────────────────────┘ ▼ MySQL / PostgreSQL / Redis ...一句话记忆Controller 接请求 → Service 写逻辑 → Repository 访问数据 → Entity 映射表。二、各层职责详解1. Controller 层控制层Controller 是系统的「门面」直接面对前端或外部调用方。应该做的事定义 REST 接口路径如GET /api/users/{id}接收并绑定请求参数RequestBody、PathVariable等做基础的参数格式校验非空、长度、格式调用 Service把结果包装成统一响应如{ code, message, data }处理 HTTP 状态码200、400、401、404 等不应该做的事写 SQL 或直接操作数据库实现复杂业务规则如「订单金额满 100 减 10 且每人限一次」在 Controller 里开事务、做分布式锁示例伪代码RestControllerRequestMapping(/api/users)publicclassUserController{privatefinalUserServiceuserService;GetMapping(/{id})publicApiResponseUserDTOgetUser(PathVariableLongid){UserDTOuseruserService.getUserById(id);returnApiResponse.ok(user);}PostMappingpublicApiResponseUserDTOcreateUser(ValidRequestBodyCreateUserRequestrequest){UserDTOuseruserService.createUser(request);returnApiResponse.ok(user);}}Controller 应该薄几行代码完成「接参 → 调 Service → 返回」业务细节全部下沉。2. Service 层业务逻辑层Service 是系统的「大脑」承载绝大部分业务价值。应该做的事实现业务规则与流程注册、下单、支付、退款编排多个 Repository 或外部服务发邮件、调支付网关管理事务边界Transactional做业务级校验用户名是否重复、库存是否足够、用户是否有权限对象转换Entity ↔ DTO有时交给专门的 Mapper 工具不应该做的事解析 HTTP 请求头、Cookie那是 Controller / Filter 的事直接写 JDBC 或拼接 SQL交给 Repository返回 HTTP 响应结构返回业务对象或 DTO 即可示例伪代码ServiceRequiredArgsConstructorpublicclassOrderService{privatefinalOrderRepositoryorderRepository;privatefinalProductRepositoryproductRepository;privatefinalInventoryServiceinventoryService;TransactionalpublicOrderDTOcreateOrder(CreateOrderRequestrequest,LonguserId){ProductproductproductRepository.findById(request.getProductId()).orElseThrow(()-newNotFoundException(商品不存在));if(product.getStock()request.getQuantity()){thrownewBusinessException(库存不足);}inventoryService.deductStock(product.getId(),request.getQuantity());OrderordernewOrder();order.setUserId(userId);order.setProductId(product.getId());order.setAmount(product.getPrice()*request.getQuantity());order.setStatus(CREATED);orderRepository.save(order);returnOrderDTO.from(order);}}Service 方法通常对应一个用例Use Case「创建订单」「取消订单」「用户登录」—— 一个方法讲清楚一件事。3. Repository 层数据访问层Repository或传统叫法 DAO负责与持久化存储打交道。应该做的事对 Entity 做增删改查CRUD封装查询条件按 ID、按状态、分页、排序对接 ORM 框架JPA、MyBatis、MyBatis-Plus或 Redis 等把「怎么查库」的细节藏起来对 Service 暴露语义清晰的方法不应该做的事写业务规则「库存不足不能下单」应在 Service决定事务是否提交事务在 Service 层声明返回 HTTP 响应或 DTO通常返回 Entity 或OptionalEntity示例JPA 风格publicinterfaceOrderRepositoryextendsJpaRepositoryOrder,Long{ListOrderfindByUserIdAndStatus(LonguserId,Stringstatus);OptionalOrderfindByOrderNo(StringorderNo);}示例MyBatis-Plus 风格RepositoryRequiredArgsConstructorpublicclassOrderRepository{privatefinalOrderMappermapper;publicOptionalOrderfindById(Longid){returnOptional.ofNullable(mapper.selectById(id));}publicvoidsave(Orderorder){if(order.getId()null){mapper.insert(order);}else{mapper.updateById(order);}}}Repository 的方法名最好表达业务含义而不是暴露 SQL 细节例如findActiveUsersByDeptId优于selectFromUserWhereStatusEquals1。4. Entity 层实体层Entity 是数据库表在 Java 世界里的「镜像」。应该做的事字段与表列一一对应或通过 ORM 注解映射承载数据的 getter/setter 或 record 构造可包含简单的、与自身数据强相关的行为如isExpired()不应该做的事调用 Service 或 Repository避免循环依赖写复杂业务流程直接暴露给前端敏感字段如passwordHash不应原样返回示例TableName(orders)publicclassOrder{TableId(typeIdType.AUTO)privateLongid;privateLonguserId;privateLongproductId;privateBigDecimalamount;privateStringstatus;privateLocalDateTimecreatedAt;// getter / setter ...}Entity 关注的是数据长什么样而不是业务流程怎么走。三、一次完整请求的流转以「用户查询自己的订单列表」为例前端 GET /api/orders?statusPAID │ ▼ ┌─────────────────────┐ │ OrderController │ 解析 status 参数获取当前登录 userId └──────────┬──────────┘ │ orderService.listByUser(userId, status) ▼ ┌─────────────────────┐ │ OrderService │ 校验 userId 合法调用 Repository 查询 └──────────┬──────────┘ │ orderRepository.findByUserIdAndStatus(...) ▼ ┌─────────────────────┐ │ OrderRepository │ 执行 SQL / Mapper 查询 └──────────┬──────────┘ │ 返回 ListOrder ▼ ┌─────────────────────┐ │ Order (Entity) │ 内存中的订单对象列表 └──────────┬──────────┘ │ Service 转为 ListOrderDTO ▼ ┌─────────────────────┐ │ OrderController │ 包装为 ApiResponse 返回 JSON └─────────────────────┘数据方向请求向下传Controller → Service → Repository → DB结果向上返Entity → Service转 DTO→ Controller → JSON四、DTO不算一层但很重要实际项目里常见dto包它不是第五层而是跨层传输的数据载体类型用途示例Request DTO接收前端入参CreateUserRequest、LoginRequestResponse DTO返回给前端的数据UserProfileDTO、OrderSummaryDTO为什么 Entity 不直接返回给前端安全Entity 可能含密码哈希、内部状态字段解耦表结构变更不应迫使 API 契约一起变聚合一个响应可能需要多张表的数据Entity 单表映射不够用前端 ── Request DTO ──► Controller ──► Service │ Entity ◄──► Repository ◄──► DB │ 前端 ◄── Response DTO ◄── Controller ◄── Service五、四层 vs 三层有什么区别有人会把 Repository 和 Entity 合并称为「Model 层」于是变成三层架构三层四层ControllerControllerServiceServiceModelEntity DAO 混在一起Repository Entity 分开四层更细的好处Entity 只描述「数据是什么」Repository 只描述「数据怎么存取」职责更清晰大项目更好维护小项目用三层也能跑团队变大、表变多、查询变复杂时拆成四层更常见。六、常见误区误区 1Controller 里写业务逻辑// ❌ 反例PostMapping(/register)publicApiResponse?register(RequestBodyRegisterRequestreq){if(userMapper.existsByEmail(req.getEmail())){returnApiResponse.fail(邮箱已注册);}UserusernewUser();user.setEmail(req.getEmail());user.setPasswordHash(passwordEncoder.encode(req.getPassword()));userMapper.insert(user);returnApiResponse.ok();}注册流程涉及校验、加密、持久化、发欢迎邮件——这些都应放在UserService.register()里。误区 2Service 直接写 SQL// ❌ 反例publicListUsersearchUsers(Stringkeyword){returnjdbcTemplate.query(SELECT * FROM users WHERE name LIKE ?,keyword);}SQL 应封装在 RepositoryService 只调用userRepository.searchByKeyword(keyword)。误区 3Entity 里注入 Service// ❌ 反例 — 容易造成循环依赖publicclassOrder{AutowiredprivateOrderServiceorderService;publicvoidcancel(){orderService.cancelOrder(this.getId());}}「取消订单」是业务行为放在OrderService.cancelOrder(id)Entity 保持纯数据对象。误区 4Repository 返回 DTORepository 应返回 Entity或简单值类型。Entity → DTO 的转换通常在 Service 层完成保持数据访问层与 API 契约解耦。误区 5层与层之间「跨层调用」❌ Controller 直接调 Repository ❌ Repository 调 Service原则上层可以调下层下层不能调上层同层之间谨慎互相调用。合法调用链Controller → Service → Repository → DB七、分层带来的实际好处1. 可测试性Service 可以用 Mock Repository 单测业务逻辑不依赖数据库Repository 可以用集成测试验证 SQL 正确性Controller 可以用 MockMvc 测接口契约2. 可维护性改表结构 → 主要动 Entity Repository改业务规则 → 主要动 Service改 API 格式 → 主要动 Controller DTO改动范围可控不容易「改一处崩全局」。3. 团队协作前端对接口、后端 A 写 Service、后端 B 写 Repository边界清晰并行开发冲突少。4. 技术替换从 MyBatis 换 JPA、从 MySQL 换 PostgreSQL往往只需改 Repository 实现Service 和 Controller 基本不动。八、实践建议1. 包结构按层划分com.example.app ├── controller ├── service │ └── impl // 可选接口 实现分离 ├── repository ├── entity ├── dto │ ├── request │ └── response ├── config └── exception2. 方法命名体现层级层命名风格示例ControllerHTTP 语义getUser、createOrder、deleteCommentService业务语义registerUser、placeOrder、approveRefundRepository数据语义findById、save、deleteByUserId3. 事务放在 ServiceServicepublicclassTransferService{Transactional// 转账涉及扣款 入账必须同一事务publicvoidtransfer(LongfromId,LongtoId,BigDecimalamount){accountRepository.deduct(fromId,amount);accountRepository.credit(toId,amount);}}4. 统一异常处理在 Controller 层之上用RestControllerAdvice捕获BusinessException转成统一 JSON避免每个 Controller 里写重复的 try-catch。5. 小项目也要分层但可以简化不必强行 Interface Impl 两套 ServiceRepository 可以直接用 MyBatis-Plus 的BaseMapper不必每个表都包一层但Controller 不写业务、Service 不写 SQL这条底线建议守住九、与 DDD、微服务的关系拓展四层架构是经典分层不是唯一方案DDD领域驱动设计会在 Service 之上再强调 Domain 层领域模型、聚合根Entity 升级为 Rich Domain Model微服务里每个服务内部仍常用四层服务之间通过 HTTP / MQ 通信不再共享 Repository学四层是地基项目复杂后可以在 Service 层内部再引入 DDD 战术模式但「Controller 薄、持久化隔离」的思路不变。十、总结层一句话Controller对外接口薄层只负责 HTTPService业务核心事务与规则在这里Repository数据网关屏蔽 SQL 和存储细节Entity表结构映射纯数据对象四层架构的本质不是「多建几个包」而是单一职责 依赖方向清晰表现与业务分离业务与持久化分离API 契约与数据库结构分离。刚开始会觉得多写几个类麻烦项目代码过万行、多人协作、需求频繁变更时这种结构会显著降低心智负担.