1. 总体分层原则
企业级 Java Web 项目推荐 分层 + 业务域组织结构:
Controller → Service → Mapper → Database
各层职责:
| 层 | 对象 | 主要职责 |
|---|---|---|
| Controller | DTO / VO | 接收请求,返回响应 |
| Service | Entity / BO / Result | 业务处理、事务、规则校验 |
| Mapper | Entity / Result | 数据持久化、SQL 查询 |
| Database | Table | 数据存储 |
| Convert / MapperUtil | MapStruct 或手写映射 | 对象转换,VO ↔ Result / DTO ↔ Query |
2. 对象模型定义与职责
2.1 DTO(Data Transfer Object)
- 位置:
controller/dto - 用途:接口请求参数模型
- 特点:
- 与前端交互字段对应
- 只用于 Controller → Service
- 不下沉到 Mapper
- 示例:
@Data
public class OrderQueryDTO {
private Long userId;
private LocalDateTime startTime;
private LocalDateTime endTime;
}
2.2 Entity / DO(Domain Object)
- 位置:
domain/entity - 用途:数据库表映射
- 特点:
- 对应单表
- 可持久化(MyBatis-Plus CRUD)
- 不包含聚合字段
- 示例:
@TableName("`order`")
@Data
public class OrderEntity {
private Long id;
private Long userId;
private String orderNo;
private LocalDateTime createTime;
}
2.3 BO(Business Object,可选)
- 位置:
domain/bo - 用途:Service 内部业务计算模型
- 特点:
- 不映射数据库
- 用于 Service 内部传递、聚合或组合 Entity
- 示例:
@Data
public class OrderBO {
private OrderEntity order;
private BigDecimal totalAmount;
}
2.4 Result(SQL 查询结果模型)
- 位置:
mapper/result - 用途:
- 连表查询 / 聚合查询的结果
- 对应 SQL 查询结果,不属于单表
- 特点:
- 不继承 Entity
- 可用 字段复制(field copy) 或 组合(composition)
- 只读
- 示例:
2.4.1 字段复制(field copy)
@Data
public class OrderSummaryResult {
private Long orderId;
private String orderNo;
private String userNickname;
private Integer productTypeCount;
private Integer totalQuantity;
private BigDecimal totalAmount;
private LocalDateTime createTime;
}
2.4.2 组合(composition)
@Data
public class OrderSummaryResult2 {
private OrderEntity order; // 内嵌 Entity
private String userNickname;
private Integer productTypeCount;
private Integer totalQuantity;
private BigDecimal totalAmount;
}
2.5 VO(View Object)
- 位置:
controller/vo - 用途:返回前端展示
- 特点:
- 与前端字段对齐
- 可格式化时间或字段
- 示例:
@Data
public class OrderSummaryVO {
private String orderNo;
private String userNickname;
private Integer productTypeCount;
private Integer totalQuantity;
private BigDecimal totalAmount;
private String createTime;
}
3. Mapper 层规范(MyBatis-Plus)
3.1 Mapper 接口
- 位置:
mapper - 用途:
- 单表 CRUD 使用 MP 内置方法
- 连表 / 聚合 SQL 使用 XML 或
@Select注解
- 示例:
public interface OrderMapper extends BaseMapper<OrderEntity> {
List<OrderSummaryResult> selectOrderSummary(@Param("query") OrderQuery query);
List<OrderSummaryResult2> selectOrderSummary2(@Param("query") OrderQuery query);
}
3.2 SQL 示例(XML + ResultMap)
<resultMap id="orderSummaryMap" type="com.xxx.mapper.result.OrderSummaryResult2">
<association property="order" javaType="com.xxx.domain.entity.OrderEntity">
<id column="orderId" property="id"/>
<result column="orderNo" property="orderNo"/>
<result column="createTime" property="createTime"/>
<result column="userId" property="userId"/>
</association>
<result column="userNickname" property="userNickname"/>
<result column="productTypeCount" property="productTypeCount"/>
<result column="totalQuantity" property="totalQuantity"/>
<result column="totalAmount" property="totalAmount"/>
</resultMap>
<select id="selectOrderSummary2" resultMap="orderSummaryMap">
SELECT
o.id AS orderId,
o.order_no AS orderNo,
u.nickname AS userNickname,
COUNT(DISTINCT oi.product_id) AS productTypeCount,
SUM(oi.quantity) AS totalQuantity,
SUM(oi.quantity * oi.price) AS totalAmount,
o.create_time AS createTime,
o.user_id AS userId
FROM `order` o
JOIN user u ON o.user_id = u.id
JOIN order_item oi ON o.id = oi.order_id
<where>
<if test="query.userId != null">
AND o.user_id = #{query.userId}
</if>
<if test="query.startTime != null">
AND o.create_time >= #{query.startTime}
</if>
<if test="query.endTime != null">
AND o.create_time <= #{query.endTime}
</if>
</where>
GROUP BY o.id, o.order_no, u.nickname, o.create_time
ORDER BY o.create_time DESC
</select>
4. Service 层规范
- 位置:
service+service/impl - 职责:
- 接收 DTO → 转 Query / BO
- 调用 Mapper → 返回 Result
- 转换 Result → VO
- 示例:
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderMapper orderMapper;
public List<OrderSummaryVO> getOrderSummary(OrderQueryDTO dto) {
OrderQuery query = new OrderQuery();
query.setUserId(dto.getUserId());
query.setStartTime(dto.getStartTime());
query.setEndTime(dto.getEndTime());
List<OrderSummaryResult> results = orderMapper.selectOrderSummary(query);
return results.stream().map(r -> {
OrderSummaryVO vo = new OrderSummaryVO();
vo.setOrderNo(r.getOrderNo());
vo.setUserNickname(r.getUserNickname());
vo.setProductTypeCount(r.getProductTypeCount());
vo.setTotalQuantity(r.getTotalQuantity());
vo.setTotalAmount(r.getTotalAmount());
vo.setCreateTime(r.getCreateTime().toString());
return vo;
}).toList();
}
public List<OrderSummaryVO> getOrderSummary2(OrderQueryDTO dto) {
OrderQuery query = new OrderQuery();
query.setUserId(dto.getUserId());
query.setStartTime(dto.getStartTime());
query.setEndTime(dto.getEndTime());
List<OrderSummaryResult2> results = orderMapper.selectOrderSummary2(query);
return results.stream().map(r -> {
OrderSummaryVO vo = new OrderSummaryVO();
vo.setOrderNo(r.getOrder().getOrderNo());
vo.setUserNickname(r.getUserNickname());
vo.setProductTypeCount(r.getProductTypeCount());
vo.setTotalQuantity(r.getTotalQuantity());
vo.setTotalAmount(r.getTotalAmount());
vo.setCreateTime(r.getOrder().getCreateTime().toString());
return vo;
}).toList();
}
}
5. Controller 层规范
@RestController
@RequestMapping("/orders")
@RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
@PostMapping("/summary")
public List<OrderSummaryVO> summary(@RequestBody OrderQueryDTO dto){
return orderService.getOrderSummary(dto); // field copy
// return orderService.getOrderSummary2(dto); // composition
}
}
6. 对象流转图(MP + 聚合 + 连表)
Frontend
↓ JSON
OrderQueryDTO
↓
Service
↓ DTO → Query
OrderQuery
↓
Mapper (连表 + 聚合)
↓
OrderSummaryResult / OrderSummaryResult2
↓ Service 转换
OrderSummaryVO
↓
Frontend
7. 目录结构规范
src/main/java/com/xxx/project
├─ controller
│ ├─ OrderController.java
│ ├─ dto
│ │ └─ OrderQueryDTO.java
│ └─ vo
│ └─ OrderSummaryVO.java
├─ service
│ ├─ OrderService.java
│ └─ impl
│ └─ OrderServiceImpl.java
├─ domain
│ ├─ entity
│ │ ├─ OrderEntity.java
│ │ ├─ UserEntity.java
│ │ └─ OrderItemEntity.java
│ └─ bo
│ └─ OrderBO.java
├─ mapper
│ ├─ OrderMapper.java
│ └─ result
│ ├─ OrderSummaryResult.java
│ └─ OrderSummaryResult2.java
└─ convert
└─ OrderConvert.java
8. 重要团队规范总结
- DTO 永远不下沉到 Mapper
- Entity / DO 对应表结构
- Result 对应 SQL 查询(连表/聚合)
- Result 不继承 Entity
- VO 只面向前端
- Service 做转换 / 聚合逻辑
- Mapper SQL 字段尽量和 Result 一一对应
- 复杂 Result 可采用组合(内嵌 Entity),简单 Result 用字段复制
- 所有层都明确职责,任何对象跨层使用都需审查

正文完
