Spring Boot 中controller、service、mapper

Spring Boot 项目中常见的 controllerservicemapper 并不是 Spring Boot 强制规定的目录,而是一种便于职责分离的工程组织方式。Spring Boot 本身不要求固定代码结构,但建议把启动类放在根包下,使组件扫描能够覆盖下面的业务包。

最核心的调用链理解为:

复制代码
Vue 前端
   ↓ HTTP 请求
Controller
   ↓
Service
   ↓
Mapper / Repository 或外部服务 Client
   ↓
SQL Server / bof-ml-service
   ↓
结果逐层返回

一个更加完整的调用链是:

复制代码
浏览器
  ↓
Filter
  ↓
Spring Security
  ↓
DispatcherServlet
  ↓
Interceptor
  ↓
Controller
  ↓
Service
  ├─ Mapper → SQL Server
  ├─ Client → bof-ml-service
  └─ 其他 Service
  ↓
DTO / VO 转换
  ↓
Controller
  ↓
前端

一、完整的常见分层

1. Controller:接口接入层

Controller 是后端系统接收 HTTP 请求的入口。

例如:

复制代码
@RestController
@RequestMapping("/api/v1/predict")
@RequiredArgsConstructor
public class EndpointPredictionController {

    private final EndpointPredictionService predictionService;

    @PostMapping("/endpoint")
    public ApiResponse<EndpointPredictionVO> predict(
            @Valid @RequestBody EndpointPredictionRequest request) {

        EndpointPredictionVO result =
                predictionService.predict(request);

        return ApiResponse.success(result);
    }
}

它主要负责:

复制代码
接收请求
解析 URL 和 JSON
执行基础参数校验
调用 Service
把结果转换为 HTTP 响应

它不应该负责:

复制代码
直接写 SQL
直接操作 Mapper
构造复杂模型特征
处理大段业务判断
直接加载机器学习模型

@RestController 会将方法返回值直接写入响应体,@PostMapping@GetMapping 等注解负责把 HTTP 请求映射到具体方法。(Home)

Controller 可以理解为:

后端系统的业务接口接待窗口。


2. Request DTO:请求参数对象

前端传来的 JSON 通常不要直接映射到数据库实体,而是映射为专门的请求对象。

例如:

复制代码
@Data
public class EndpointPredictionRequest {

    @NotBlank(message = "炉次号不能为空")
    private String heatId;
}

前端发送:

复制代码
{
  "heatId": "H20260510-001"
}

Spring 会将它转换成:

复制代码
EndpointPredictionRequest

Request DTO 的职责是:

复制代码
定义接口允许接收哪些字段
进行参数校验
隔离前端接口与数据库结构

常见命名包括:

复制代码
EndpointPredictionRequest
EndpointPredictionDTO
CreateHeatRequest
HeatQueryRequest
ModelActivateCommand

推荐在项目中把不同用途区分清楚:

复制代码
Request:Controller 接收的请求
Response / VO:Controller 返回的数据
DTO:不同层之间传递的数据
Entity / DO:数据库表映射对象

3. Service:业务逻辑层

Service 是整个业务系统中最重要的一层。

例如:

复制代码
public interface EndpointPredictionService {

    EndpointPredictionVO predict(
            EndpointPredictionRequest request);
}

实现类:

复制代码
@Service
@RequiredArgsConstructor
public class EndpointPredictionServiceImpl
        implements EndpointPredictionService {

    private final HeatService heatService;
    private final MlServiceClient mlServiceClient;
    private final ModelResultMapper modelResultMapper;
    private final EndpointPredictionConverter converter;

    @Override
    @Transactional
    public EndpointPredictionVO predict(
            EndpointPredictionRequest request) {

        HeatEntity heat =
                heatService.requireHeat(request.getHeatId());

        EndpointMlResponse mlResponse =
                mlServiceClient.predictEndpoint(
                        request.getHeatId());

        ModelResultEntity result =
                converter.toEntity(heat, mlResponse);

        modelResultMapper.insert(result);

        return converter.toVO(result);
    }
}

Service 主要负责:

复制代码
业务流程编排
业务规则判断
调用多个 Mapper
调用其他 Service
调用外部 Python 服务
事务控制
异常转换
结果组装

在你的终点预测场景中,Service 要表达的是:

复制代码
1. 检查炉次是否存在
2. 检查炉次状态是否允许预测
3. 调用 bof-ml-service
4. 判断模型服务是否成功
5. 保存预测结果
6. 返回前端需要的数据

Service 不应该关注:

复制代码
HTTP 状态如何映射
SQL 具体怎么写
JSON 如何序列化
XGBoost 模型怎样执行

为什么经常有 Service 和 ServiceImpl

常见结构是:

复制代码
EndpointPredictionService
EndpointPredictionServiceImpl

其中:

复制代码
Service 接口:定义系统能做什么
ServiceImpl:定义具体怎么做

接口形式适用于:

  • 可能存在多个实现;

  • 需要便于测试;

  • 需要使用代理或扩展;

  • 希望降低调用方与实现的耦合。

小型项目也可以直接使用:

复制代码
@Service
public class EndpointPredictionService {
}

不一定必须拆成接口和实现类。


4. Mapper:数据库访问层

如果项目使用 MyBatis,Mapper 通常是数据库操作接口。

例如:

复制代码
@Mapper
public interface HeatMapper {

    HeatEntity selectByHeatId(String heatId);

    int insert(HeatEntity entity);

    int updateStatus(
            @Param("heatId") String heatId,
            @Param("status") String status);
}

MyBatis Spring Boot Starter 默认会扫描带有 @Mapper 的接口,也可以使用 @MapperScan 统一扫描 Mapper 包。(mybatis.org)

Mapper 对应的 XML:

复制代码
<mapper namespace="com.converter.heat.mapper.HeatMapper">

    <select id="selectByHeatId"
            resultType="com.converter.heat.entity.HeatEntity">
        SELECT
            heat_id,
            steel_grade,
            converter_no,
            blow_start_time,
            main_blow_end_time
        FROM bof_heat
        WHERE heat_id = #{heatId}
    </select>

</mapper>

Mapper 负责:

复制代码
执行 SQL
查询数据库
插入数据
更新数据
删除数据
将查询结果映射为 Java 对象

Mapper 不应该负责:

复制代码
判断炉次能不能预测
决定使用哪个模型
组织多步骤业务流程
调用 Python 模型服务
拼装前端页面数据

可以把 Mapper 理解为:

Java 代码访问 SQL Server 的直接通道。


5. Mapper XML:SQL 定义文件

MyBatis 项目中,较复杂 SQL 通常放在:

复制代码
src/main/resources/mapper/

例如:

复制代码
resources/
└── mapper/
    ├── HeatMapper.xml
    ├── ModelResultMapper.xml
    └── ModelRegistryMapper.xml

配置:

复制代码
mybatis:
  mapper-locations: classpath*:mapper/**/*.xml
  type-aliases-package: com.converter.system

Mapper XML 负责:

复制代码
SQL 语句
结果字段映射
动态查询条件
分页条件
批量插入
复杂 JOIN

例如:

复制代码
<select id="selectLatestPrediction"
        resultType="ModelResultEntity">
    SELECT TOP 1
        *
    FROM bof_model_result
    WHERE heat_id = #{heatId}
      AND task_key = #{taskKey}
      AND model_version = #{modelVersion}
      AND input_hash = #{inputHash}
    ORDER BY created_at DESC
</select>

二、数据库对象相关的几个概念

6. Entity / DO / PO:数据库实体

这几个名字经常表示相似概念。

例如:

复制代码
@Data
@TableName("bof_heat")
public class HeatEntity {

    private Long id;
    private String heatId;
    private String steelGrade;
    private String converterNo;
    private LocalDateTime blowStartTime;
    private LocalDateTime mainBlowEndTime;
}

常见名称:

复制代码
Entity:实体对象
DO:Data Object
PO:Persistent Object

它们通常表示:

与数据库表结构接近的 Java 对象。

例如数据库表:

复制代码
bof_heat

对应:

复制代码
HeatEntity

数据库字段:

复制代码
heat_id
steel_grade
main_blow_end_time

对应 Java 字段:

复制代码
heatId
steelGrade
mainBlowEndTime

Entity 不应该直接承担:

复制代码
前端请求结构
前端响应结构
复杂业务计算结果
模型服务的完整响应

因为数据库结构一旦发生变化,不应该强迫前端接口一起改变。


7. DTO:数据传输对象

DTO 是不同层之间传递数据的对象。

例如,从 Java 发往 Python 模型服务:

复制代码
@Data
@Builder
public class EndpointMlRequestDTO {

    private String requestId;
    private String heatId;
    private String schemaVersion;
}

从 Python 返回:

复制代码
@Data
public class EndpointMlResponseDTO {

    private String requestId;
    private String modelName;
    private String modelVersion;
    private PredictionDTO prediction;
}

其中:

复制代码
@Data
public class PredictionDTO {

    private BigDecimal carbon;
    private BigDecimal manganese;
    private BigDecimal phosphorus;
    private BigDecimal sulfur;
    private BigDecimal temperature;
}

DTO 的特点是:

复制代码
为一次数据传递服务
不必与数据库表一致
可以组合多个来源的数据
可以只保留调用所需字段

8. VO / Response:返回前端的对象

VO 通常表示:

复制代码
View Object

即专门提供给前端页面的数据。

例如:

复制代码
@Data
@Builder
public class EndpointPredictionVO {

    private String heatId;
    private String steelGrade;

    private BigDecimal carbon;
    private BigDecimal manganese;
    private BigDecimal phosphorus;
    private BigDecimal sulfur;
    private BigDecimal temperature;

    private String modelName;
    private String modelVersion;
    private Boolean fallbackUsed;
    private LocalDateTime predictedAt;
}

最终返回:

复制代码
{
  "heatId": "H20260510-001",
  "steelGrade": "Q235B",
  "carbon": 0.052,
  "manganese": 0.183,
  "phosphorus": 0.016,
  "sulfur": 0.009,
  "temperature": 1662,
  "modelName": "endpoint-xgb",
  "modelVersion": "v2",
  "fallbackUsed": false
}

为什么不直接返回 Entity?

因为数据库 Entity 可能包含:

复制代码
数据库主键
内部状态
输入哈希
内部错误信息
创建者
逻辑删除标记
不适合向前端暴露的字段

VO 只保留页面真正需要的数据。


三、对象转换层

9. Converter / Assembler

Entity、DTO、VO 之间需要转换。

例如:

复制代码
@Component
public class EndpointPredictionConverter {

    public ModelResultEntity toEntity(
            String heatId,
            EndpointMlResponseDTO response) {

        ModelResultEntity entity =
                new ModelResultEntity();

        entity.setHeatId(heatId);
        entity.setTaskKey("endpoint_prediction");
        entity.setModelName(response.getModelName());
        entity.setModelVersion(response.getModelVersion());
        entity.setCarbon(
                response.getPrediction().getCarbon());
        entity.setTemperature(
                response.getPrediction().getTemperature());

        return entity;
    }

    public EndpointPredictionVO toVO(
            ModelResultEntity entity) {

        return EndpointPredictionVO.builder()
                .heatId(entity.getHeatId())
                .carbon(entity.getCarbon())
                .temperature(entity.getTemperature())
                .modelName(entity.getModelName())
                .modelVersion(entity.getModelVersion())
                .predictedAt(entity.getCreatedAt())
                .build();
    }
}

常见名称包括:

复制代码
Converter
Assembler
Mapper
StructMapper

这里要注意,"Mapper"有两种含义:

MyBatis Mapper

复制代码
HeatMapper
ModelResultMapper

负责数据库操作。

对象转换 Mapper

复制代码
HeatStructMapper
EndpointPredictionStructMapper

负责 Java 对象转换,常见于 MapStruct。

为了避免混淆,建议你的项目中:

复制代码
数据库访问统一叫 Mapper
对象转换统一叫 Converter

四、外部服务调用层

10. Client / Gateway / Adapter

你的 Java 系统需要调用:

复制代码
bof-ml-service

因此可以专门建立外部服务调用层。

例如:

复制代码
public interface MlServiceClient {

    EndpointMlResponseDTO predictEndpoint(
            String heatId);
}

实现:

复制代码
@Component
@RequiredArgsConstructor
public class MlServiceHttpClient
        implements MlServiceClient {

    private final WebClient mlWebClient;

    @Override
    public EndpointMlResponseDTO predictEndpoint(
            String heatId) {

        EndpointMlRequestDTO request =
                EndpointMlRequestDTO.builder()
                        .heatId(heatId)
                        .build();

        return mlWebClient.post()
                .uri("/api/v1/predict/endpoint")
                .bodyValue(request)
                .retrieve()
                .bodyToMono(
                    EndpointMlResponseDTO.class)
                .block();
    }
}

它负责:

复制代码
构造 HTTP 请求
调用 Python 服务
设置超时时间
解析响应
处理网络异常

它不负责:

复制代码
判断炉次是否允许预测
保存业务结果
决定页面显示什么

可以有:

复制代码
MlServiceClient
MesClient
LaboratoryClient
OpcClient
FileStorageClient

11. Adapter:适配层

Adapter 通常用于屏蔽不同系统之间的数据格式差异。

例如 Python 返回:

复制代码
{
  "prediction": {
    "C": 0.052,
    "temperature": 1662
  }
}

Java 内部希望使用:

复制代码
EndpointPrediction

Adapter 可以完成:

复制代码
Python 字段格式
       ↓
Java 领域对象格式

例如:

复制代码
@Component
public class MlPredictionAdapter {

    public EndpointPrediction adapt(
            EndpointMlResponseDTO response) {

        return new EndpointPrediction(
                response.getPrediction().getCarbon(),
                response.getPrediction().getTemperature(),
                response.getModelVersion());
    }
}

五、异常处理相关层

12. Exception:业务异常

例如炉次不存在:

复制代码
public class HeatNotFoundException
        extends RuntimeException {

    public HeatNotFoundException(String heatId) {
        super("炉次不存在:" + heatId);
    }
}

没有激活模型:

复制代码
public class ModelNotActiveException
        extends RuntimeException {

    public ModelNotActiveException(String taskKey) {
        super("任务没有激活模型:" + taskKey);
    }
}

模型服务超时:

复制代码
public class MlServiceTimeoutException
        extends RuntimeException {
}

13. GlobalExceptionHandler:全局异常处理

不要在每个 Controller 中重复写:

复制代码
try {
    ...
} catch (Exception e) {
    ...
}

可以统一处理:

复制代码
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(HeatNotFoundException.class)
    public ResponseEntity<ApiResponse<Void>>
            handleHeatNotFound(
                    HeatNotFoundException exception) {

        return ResponseEntity.status(404)
                .body(ApiResponse.failure(
                        "HEAT_NOT_FOUND",
                        exception.getMessage()));
    }

    @ExceptionHandler(MlServiceTimeoutException.class)
    public ResponseEntity<ApiResponse<Void>>
            handleMlTimeout(
                    MlServiceTimeoutException exception) {

        return ResponseEntity.status(504)
                .body(ApiResponse.failure(
                        "ML_SERVICE_TIMEOUT",
                        "模型服务响应超时"));
    }
}

这样所有 Controller 的错误结构保持一致。


六、通用响应结构

14. ApiResponse

常见统一返回结构:

复制代码
@Data
@AllArgsConstructor
public class ApiResponse<T> {

    private int code;
    private String message;
    private T data;
    private String requestId;

    public static <T> ApiResponse<T> success(T data) {
        return new ApiResponse<>(
                200,
                "success",
                data,
                RequestContext.getRequestId());
    }

    public static <T> ApiResponse<T> failure(
            String code,
            String message) {

        return new ApiResponse<>(
                500,
                message,
                null,
                RequestContext.getRequestId());
    }
}

前端收到:

复制代码
{
  "code": 200,
  "message": "success",
  "data": {
    "heatId": "H20260510-001",
    "temperature": 1662
  },
  "requestId": "req-20260717-001"
}

七、配置相关层

15. Config:配置类

用于创建和配置 Spring Bean。

例如模型服务 HTTP 客户端:

复制代码
@Configuration
public class MlServiceConfig {

    @Bean
    public WebClient mlWebClient(
            MlServiceProperties properties) {

        return WebClient.builder()
                .baseUrl(properties.getBaseUrl())
                .build();
    }
}

配置文件:

复制代码
bof:
  ml-service:
    base-url: http://127.0.0.1:18080
    connect-timeout: 2s
    read-timeout: 10s

属性类:

复制代码
@Data
@ConfigurationProperties(prefix = "bof.ml-service")
public class MlServiceProperties {

    private String baseUrl;
    private Duration connectTimeout;
    private Duration readTimeout;
}

常见 Config:

复制代码
WebConfig
MyBatisConfig
SecurityConfig
JacksonConfig
CorsConfig
WebClientConfig
RedisConfig
TransactionConfig

八、安全相关层

16. Security

Security 负责:

复制代码
用户登录
Token 验证
接口权限
角色判断
管理员操作控制

例如:

复制代码
@PreAuthorize("hasRole('OPERATOR')")
@PostMapping("/endpoint")
public ApiResponse<EndpointPredictionVO> predict(...) {
}

模型激活接口:

复制代码
@PreAuthorize("hasRole('MODEL_ADMIN')")
@PostMapping("/models/{modelId}/activate")
public ApiResponse<Void> activate(...) {
}

普通操作人员可以:

复制代码
执行预测
查看预测结果

模型管理员可以:

复制代码
上传模型
验证模型
激活模型
回滚模型
下架模型

九、请求预处理层

17. Filter

Filter 工作在 Servlet 层,通常早于 Controller。

常见用途:

复制代码
生成 request_id
读取 Token
跨域处理
请求日志
编码处理
请求体包装

例如:

复制代码
@Component
public class RequestIdFilter
        extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain filterChain)
            throws ServletException, IOException {

        String requestId =
                UUID.randomUUID().toString();

        MDC.put("requestId", requestId);
        response.setHeader(
                "X-Request-Id",
                requestId);

        try {
            filterChain.doFilter(
                    request,
                    response);
        } finally {
            MDC.remove("requestId");
        }
    }
}

18. Interceptor

Interceptor 位于 Spring MVC 内部,主要围绕 Controller 工作。

常见用途:

复制代码
登录检查
权限补充检查
操作日志
接口耗时
炉次上下文
重复提交限制

例如:

复制代码
@Component
public class ApiTimingInterceptor
        implements HandlerInterceptor {

    @Override
    public boolean preHandle(
            HttpServletRequest request,
            HttpServletResponse response,
            Object handler) {

        request.setAttribute(
                "startTime",
                System.currentTimeMillis());

        return true;
    }

    @Override
    public void afterCompletion(
            HttpServletRequest request,
            HttpServletResponse response,
            Object handler,
            Exception ex) {

        long elapsed =
                System.currentTimeMillis()
                - (long) request.getAttribute("startTime");
    }
}

19. AOP / Aspect

AOP 用于处理横切逻辑,即多个业务方法都会出现、但不属于核心业务的内容。

例如:

复制代码
操作日志
接口耗时
权限注解
幂等处理
数据权限
模型调用统计

示例:

复制代码
@Aspect
@Component
public class ModelOperationAspect {

    @Around("@annotation(ModelOperationLog)")
    public Object record(ProceedingJoinPoint point)
            throws Throwable {

        long start = System.currentTimeMillis();

        try {
            return point.proceed();
        } finally {
            long elapsed =
                    System.currentTimeMillis() - start;
        }
    }
}

十、事务层

20. Transaction

事务通常放在 Service 层。

例如激活模型时,需要同时完成:

复制代码
旧模型 active → inactive
新模型 staged → active
更新 bof_model_route
写入部署事件

这些操作应该作为一个整体成功或失败:

复制代码
@Service
@RequiredArgsConstructor
public class ModelActivationService {

    private final ModelCatalogMapper catalogMapper;
    private final ModelRouteMapper routeMapper;
    private final DeploymentEventMapper eventMapper;

    @Transactional
    public void activate(Long modelId) {

        ModelCatalogEntity model =
                catalogMapper.selectById(modelId);

        routeMapper.switchActiveModel(
                model.getTaskKey(),
                modelId);

        catalogMapper.updateStatus(
                modelId,
                "active");

        eventMapper.insert(
                DeploymentEventEntity.activate(modelId));
    }
}

Spring 提供统一的事务抽象,并支持声明式事务;@Transactional 通常用于 Service 层的业务边界。(Home)

不要把事务主要放在 Controller:

复制代码
@PostMapping(...)
@Transactional // 不推荐作为主要业务事务边界

更合理的是:

复制代码
Controller
   ↓
@Transactional Service
   ↓
多个 Mapper 操作

十一、其他常见目录

21. Enum

保存固定枚举值:

复制代码
public enum ModelStatus {
    UPLOADED,
    VALIDATING,
    STAGED,
    ACTIVE,
    INACTIVE,
    RETIRED,
    VALIDATION_FAILED,
    LOAD_FAILED
}

优点是避免代码中到处出现:

复制代码
"active"
"inactive"
"staged"

22. Constant

保存固定常量:

复制代码
public final class ModelTaskKeys {

    public static final String ENDPOINT_PREDICTION =
            "endpoint_prediction";

    public static final String SLAG_PREDICTION =
            "slag_prediction";

    private ModelTaskKeys() {
    }
}

23. Util

用于无状态、通用、确实可以复用的工具方法:

复制代码
HashUtils
JsonUtils
TimeUtils
FileUtils

例如:

复制代码
public final class HashUtils {

    public static String sha256(String content) {
        // 计算 SHA-256
    }
}

不要把所有不知道放哪里的业务代码都放进 util

错误示例:

复制代码
EndpointPredictionUtil
ModelActivateUtil
HeatBusinessUtil

如果里面包含业务规则,它更适合放在:

复制代码
Service
DomainService
Converter

24. Validator

复杂业务校验可以单独提取:

复制代码
@Component
public class EndpointPredictionValidator {

    public void validate(HeatEntity heat) {

        if (heat == null) {
            throw new HeatNotFoundException();
        }

        if (heat.getMainBlowEndTime() == null) {
            throw new BusinessException(
                    "主吹尚未结束,不能执行终点预测");
        }
    }
}

Service 中调用:

复制代码
validator.validate(heat);

25. Event / Listener

当一个业务完成后,需要触发附属操作,可以使用事件。

例如模型激活成功后:

复制代码
publisher.publishEvent(
        new ModelActivatedEvent(modelId));

监听器:

复制代码
@Component
public class ModelActivatedListener {

    @EventListener
    public void handle(ModelActivatedEvent event) {
        // 更新缓存、发送通知或刷新状态
    }
}

如果事件必须在数据库事务提交成功之后执行,可以使用事务事件监听。Spring 支持通过 @TransactionalEventListener 将事件监听绑定到事务阶段。(Home)


26. Scheduler / Job

用于定时任务:

复制代码
定时检查 Worker 健康状态
定时清理过期缓存
定时同步炉次数据
定时检查模型漂移

例如:

复制代码
@Component
@RequiredArgsConstructor
public class WorkerHealthJob {

    private final WorkerHealthService healthService;

    @Scheduled(fixedDelay = 60_000)
    public void checkWorkers() {
        healthService.checkAll();
    }
}

十二、常见错误分层方式

错误一:Controller 写全部逻辑

复制代码
@PostMapping("/endpoint")
public Object predict(...) {

    HeatEntity heat =
            heatMapper.selectByHeatId(...);

    if (heat == null) {
        ...
    }

    Object response =
            webClient.post()...;

    modelResultMapper.insert(...);

    return response;
}

问题是:

复制代码
Controller 过重
难以测试
难以复用
事务边界不清楚

错误二:Service 直接写 SQL

复制代码
@Service
public class EndpointService {

    public Object predict() {
        String sql = "SELECT ...";
    }
}

SQL 应放在 Mapper 或 Repository 层。


错误三:Mapper 包含业务判断

复制代码
@Mapper
public interface ModelMapper {

    boolean canActivateModel(Long modelId);
}

如果 canActivateModel 涉及:

复制代码
状态判断
健康检查
是否存在替代模型
Schema 是否兼容
Worker 是否已预热

这不是简单数据库访问,应放在 Service。


错误四:Entity 直接返回前端

复制代码
@GetMapping
public ModelResultEntity getResult() {
    return mapper.selectById(...);
}

容易把数据库内部字段暴露给前端。


错误五:ServiceImpl 只是转发

复制代码
public HeatEntity getHeat(String heatId) {
    return heatMapper.selectByHeatId(heatId);
}

简单查询可以存在,但如果所有 Service 都只是机械转发,说明分层可能过度。


十五、最容易记住的总结

复制代码
Controller:接口入口
Request:前端传了什么
Service:业务应该怎么执行
Mapper:数据库怎么读写
Entity:数据库中的数据长什么样
DTO:层与层之间传什么
VO:最终给前端展示什么
Converter:对象之间怎么转换
Client:怎么调用外部系统
Config:组件怎么配置
Validator:业务参数是否合法
Exception:失败时是什么问题
Filter:请求进入系统前做什么
Interceptor:进入 Controller 前后做什么
Aspect:公共横切逻辑怎么统一处理
Security:谁能访问接口
Transaction:多项数据库操作如何保证一致
Job:定时执行什么
Event:业务完成后触发什么

放到你的项目中,就是:

复制代码
前端提出预测请求
Controller 接收请求
Service 组织预测流程
Mapper 查询和保存业务数据
Client 调用 bof-ml-service
Python 完成特征构造和模型推理
Java Converter 整理结果
VO 返回前端

最核心的分层原则是:

Controller 不写业务,Service 不写具体 SQL,Mapper 不做业务决策,Entity 不直接代替接口对象,外部系统调用单独放在 Client 或 Adapter 层。

相关推荐
赶紧写完去睡觉6 小时前
Anaconda 创建虚拟环境与使用
python·pycharm·conda
迷途呀6 小时前
Python:函数中的参数类型
开发语言·笔记·python·langchain·nlp
你驴我7 小时前
WhatsApp 多账号下消息已读回执的实时聚合与推送实践
后端·python
記億揺晃着的那天8 小时前
NAS + Tailscale + WebDAV:安全对接云服务器 Spring Boot
spring boot·tailscale·nas·webdav
橘子海全栈攻城狮9 小时前
【最新源码】基于SpringBoot + Vue的超市管理系统的设计与实现D002
java·开发语言·vue.js·spring boot·后端·spring
AC赳赳老秦9 小时前
OpenClaw 采集任务日志审计:全程记录采集行为,满足合规溯源与企业审计要求
java·大数据·python·数据挖掘·数据分析·php·openclaw
huangdong_9 小时前
电商图片下载工具横向对比深度评测:固乔、FATKUN、图快、当图、淘蛙、存图宝七款工具全面解析
python
满怀冰雪9 小时前
04-Paddle Tensor 基础:形状、数据类型、广播与索引
python·深度学习·神经网络·paddle
cui_ruicheng9 小时前
Python从入门到实战(十五):文件操作与目录管理
开发语言·python
xcLeigh10 小时前
Doubao-Seed-Evolving大模型接入教程|搭建全品类提示词+AI工具导航网页
前端·人工智能·python·ai·html·ai开发·豆包