搭建一个SpringBoot微服务项目,写第一行代码并不难。难的是三个月后,当团队从3人扩展到10人,当服务从1个拆到8个,代码库依然能保持清晰、一致、可维护。没有规范的微服务项目,最终都会变成“能跑但没人敢改”的泥潭。以下这份代码规范,是我们从多个生产项目中沉淀出来的,从零搭建时照着做,能帮你省下大量重构时间。
一、项目结构:按业务拆分,而不是按技术分层
很多项目习惯把所有Controller放一个模块、所有Service放另一个模块。这种按技术分层的做法在微服务下会带来灾难:改一个业务功能要跨多个模块,模块间依赖错综复杂。
正确的做法是按业务领域拆分模块。一个典型的订单服务结构如下:
order-service/ ├── order-api/ # 对外暴露的DTO和Feign接口 ├── order-domain/ # 领域模型、领域服务 ├── order-infrastructure/ # 数据库、缓存、消息队列实现 └── order-boot/ # 启动模块,装配所有依赖
每个业务模块内部再按controller、service、repository分层。这样拆的好处是:业务边界清晰,模块间依赖单向,未来拆分成独立微服务时几乎不需要重构。
二、命名规范:让代码自己解释自己
类名用大驼峰,方法名和变量名用小驼峰,常量全大写下划线分隔,这些是基础。更重要的是语义化命名:
Controller以Controller结尾,Service以Service结尾,Repository以Repository结尾。
布尔类型变量不要用is开头,因为序列化时容易出问题,用enabled、deleted更安全。
DTO按用途命名:OrderCreateRequest、OrderQueryResponse、OrderDetailVO,不要用OrderDTO1、OrderDTO2。
数据库表名用下划线分隔,如order_detail,实体类用大驼峰OrderDetail。
三、统一返回格式与异常处理
微服务对外返回的JSON必须结构统一,否则前端要写无数个if-else。推荐格式:
{ "code": 0, "message": "success", "data": { ... } }用@RestControllerAdvice实现全局异常处理,把业务异常、参数校验异常、系统异常分别映射为不同的错误码。永远不要在Controller里写try-catch,业务异常直接抛出自定义BusinessException,由全局处理器统一兜底。这样Controller代码干净,异常逻辑集中管理。
四、DTO、VO、Entity严格分离
这是最容易偷懒的地方。很多项目直接用Entity接收前端参数、返回给前端,结果导致:数据库字段暴露、敏感信息泄露、前端传参污染数据库。
规范做法:
Entity只用于数据库映射,不对外暴露。
DTO用于接收前端请求,放在api模块。
VO用于返回前端,按需组装字段。
转换用MapStruct或手动Builder,不要用BeanUtils.copyProperties,字段名不一致时它不会报错,是线上事故的常客。
五、配置与依赖管理
所有配置项集中在application.yml,敏感信息用环境变量或配置中心,禁止硬编码密码和密钥。多环境用application-dev.yml、application-prod.yml区分。
依赖管理上,父POM统一管理版本号,子模块只声明groupId和artifactId。禁止在子模块中随意引入新依赖而不经过父POM版本控制,否则依赖冲突会让你在启动时收到一堆NoSuchMethodError。
六、日志与监控
日志用SLF4J,禁止用System.out.println。关键业务节点必须打日志:请求入口、外部调用前后、异常捕获处。日志格式统一包含traceId,便于链路追踪。
每个服务必须暴露/actuator/health和/actuator/metrics,接入Prometheus和Grafana。没有监控的微服务等于裸奔。
七、代码检查与测试
集成Checkstyle和SpotBugs,在CI流程中强制执行。单元测试覆盖Service核心逻辑,集成测试覆盖Controller到数据库的完整链路。测试类命名XxxServiceTest,方法命名should_do_something_when_condition。
写在最后
规范的价值不在于“看起来专业”,而在于降低协作成本。当每个人都按同样的结构写代码,Code Review会从“你这里为什么这么写”变成“这个逻辑有没有边界问题”。从零搭建时多花一天定规范,未来能省下几十天的返工时间。这份规范建议直接放进项目根目录的CONTRIBUTING.md,让新加入的人第一天就知道该怎么写。