当前位置: 首页 > news >正文

Spring Boot + MyBatis 多模块项目中,如何优雅完成一个增量需求

摘要

在老系统中做需求,最怕的不是写代码,而是不清楚应该改哪里、复用哪里、绕开哪里。本文结合一个续期管理后台中的“规则中心配置页”需求,聊聊在 Spring Boot + MyBatis 多模块项目里,如何用较小改动完成一次稳定的增量开发。

1. 背景

很多企业项目不是从零开始的,而是在已有系统上不断迭代。常见结构大概是:
project-root
├── project-api
├── project-common
├── project-core
├── project-provider
├── project-server
└── project-web
这类项目通常有几个特点:
Controller 层在 web 模块;
业务 Service 在 core 模块;
DTO、Entity、Param 在 api 模块;
通用枚举、工具类在 common 模块;
外部系统调用或消息处理在 provider/server 模块;
MyBatis Mapper 和 XML 通常集中在 core。
如果新需求一上来就大改底层逻辑,风险会非常高。更稳妥的方式是:先定位现有链路,再基于现有架构做增量封装。

2. 增量需求的拆解方式

以“新增一个配置页”为例,页面可能包含:
配置列表查询;
新增配置;
删除配置;
批量提交;
操作历史;
异常清单下载;
权限控制。
后端可以拆成以下几层:
Controller

Application Service

Domain / Legacy Service

Mapper / External Provider
关键点是:新 Service 不一定要重写业务逻辑,而是作为“编排层”存在。
例如:

publicinterfaceDataConfigService{List<ProductOption>listProductOptions(Stringkeyword);voidaddConfig(IntegerproductId,StringproductName,LongoperatorId);voiddeleteConfig(IntegerproductId,LongoperatorId);BatchResultbatchRepair(Stringids,StringoperatorNo,StringoperatorName);PageResult<ImportRecordDTO>pageRecords(RecordQueryquery);}

这个接口并不关心底层数据到底来自老表、新表、外部接口还是消息处理链路。它的职责是把页面需要的能力整理成稳定接口。

3. Controller 层只做三件事

在老项目里,Controller 很容易越写越厚。我的经验是尽量让 Controller 只做三件事:
接收参数;
获取当前操作人;
调用 Service 并返回统一结果。
示例:

@PostMapping("/config/add")publicResult<Void>addConfig(@RequestBodyConfigRequestrequest,HttpServletRequestservletRequest){Employeeemployee=getCurrentEmployee(servletRequest);dataConfigService.addConfig(request.getProductId(),request.getProductName(),Long.valueOf(employee.getId()));returnResult.success();}

这样做的好处是清晰:
参数校验在 Service 中统一处理;
操作人信息不散落在各处;
Controller 不感知底层业务细节;
后续单测更容易写。

4. 复用旧逻辑,而不是复制旧逻辑

老系统里常常已经有一些能力,比如:
根据投保单号查询主数据;
更新保单状态;
调用外部系统补齐数据;
写操作日志;
发送消息通知;
查询配置。
新增功能时,优先考虑“调用已有 Service”,而不是把旧代码复制一份过来。
错误示例:

// 把旧逻辑复制到新 Service 中,后续两边都要维护privatevoidrepairData(Longid){// 一大段复制来的老逻辑}

更好的方式:

privatevoidrepairData(Longid){legacyRepairService.repairById(id);}如果旧逻辑粒度太粗,可以在外层做适配:privatevoidrepairOne(Longid){try{legacyService.repair(id);}catch(Exceptione){thrownewBusinessException(normalizeMessage(e.getMessage()));}}

这样新增功能就变成“业务编排”,不是“底层重写”。

5. 批处理要有记录和失败明细

批量提交类需求不要只返回一句“处理完成”。更实用的模型是:
导入记录表
├── 操作类型
├── 操作人
├── 总数
├── 成功数
├── 失败数
├── 状态
└── 操作时间

失败明细表
├── 记录ID
├── 业务单号
└── 失败原因
批处理伪代码:

privateBatchResultexecuteBatch(List<Long>ids,Processorprocessor){ImportRecordrecord=createRecord(ids.size());intsuccess=0;List<ImportError>errors=newArrayList<>();for(Longid:ids){try{processor.process(id);success++;}catch(Exceptione){errors.add(buildError(record.getId(),id,e.getMessage()));}}saveErrors(errors);updateRecord(record,success,errors.size());returnbuildResult(record);}

这个设计有两个明显优势:
用户知道每次操作的结果;
失败数据可以下载后继续排查。

6. 小结

在多模块老系统中做增量需求,重点不是“写更多代码”,而是“把新功能安放在合适的位置”。
建议遵循几个原则:
Controller 保持薄;
新增 Service 做编排;
复用旧业务链路;
Mapper 只承载必要查询;
批处理必须有历史记录;
权限点跟页面和按钮保持一致;
不做无关重构。
老系统改造最重要的是稳。能小步扩展,就不要大刀阔斧。

http://www.cnnetsun.cn/news/3012764.html

相关文章:

  • 基于51单片机的智能香薰灯:从PID温控到WS2812B灯效的嵌入式开发实践
  • Spring Boot 跨服务事务实现
  • 云计算生态产品经理面试攻略:从系统思维到商业实战
  • 自动化测试平台开发
  • 推送原理:从APNs到厂商通道
  • SPC统计过程控制:从入门到实战的完整技术路线
  • Redis高级笔记:Java程序员短期面试突击必备!
  • 安达发|保健品行业aps生产排程:提升效率的关键密钥
  • 干草颗粒机公司
  • WAVES 2026大会聚焦具身智能:泡沫之下,何时真正走进现实?
  • 问题解决策略动态规划训练3
  • 不到8个月完成三轮融资!云际航电全栈自研航电系统,欲打破国际垄断
  • 3分钟配置完成:基于YOLOv5的智能中国象棋AI辅助系统
  • 一线音响品牌集体入局 HiPlay!持证硬件解锁华为全渠道供应链资源
  • OpenSSL实战指南:数字证书结构解析与全生命周期管理
  • OpenMOSS / MOSS-TTS-Nano TTS文字转语音windows本地部署
  • 小程序制作公司哪家好怎么选正规服务商?
  • 密码学实战指南:从核心原理到工程避坑,构建安全系统基石
  • 50平小店装修怎么利用空间?小店老板要先看这几点
  • 服装设计的“下限”与“上限”:AI到底改变了什么,又什么都改不了?
  • HarmonyOS技术精讲-UI开发调试调优:动画性能调优艺术
  • Pale Moon 34.3.1 发布:安全更新与漏洞修复,保障浏览体验
  • 选择合适的后端技术栈:基于项目需求的决策分析
  • 装备物资库房一体化安防管控解决方案
  • 如何轻松实现PS4游戏修改:GoldHEN金手指管理器完整指南
  • Webug4.0文件上传漏洞实战:从JS绕过到.htaccess攻击全解析
  • 【C/C++】用 epoll 写一个 Reactor:连接对象、回调和状态机
  • Tkinter库的学习记录-7
  • SEW变频器MC07B系列维修
  • Kotlin的密封类与内联类:类型安全的枚举和包装器