Java 业务项目架构指南
分层架构
四层单向调用,禁止反向或跨层依赖:
Controller → Facade → Service → Repository (Mapper)
各层职责
| 层 | 职责 | 注入 | 禁止 | |---|---|---|---| | Controller | HTTP 入口、参数校验、响应封装 | Facade | 注入 Service;包含业务逻辑;添加事务 | | Facade | 跨服务编排、事务边界、DTO 组装 | 多个 Service、Converter | 直接操作 Mapper | | Service | 单一领域业务逻辑、数据操作 | 本域 Mapper、Converter | 注入其他 Service 或其他域的 Mapper | | Repository | 数据访问(继承 BaseMapper) | — | 添加自定义方法(查询在 Service 中构建) |
为什么引入 Facade 层
- 单一职责: Service 层专注自己领域,不承担编排逻辑
- 降低耦合: Service 不依赖其他 Service,可独立测试
- 事务清晰: 跨服务事务统一在 Facade 管理
- 便于定位: 业务编排集中在 Facade,排查问题路径清晰
层间调用铁律
规则 1: Controller 只注入 Facade
// CORRECT
@RestController
@RequiredArgsConstructor
public class PolicyController {
private final PolicyFacade facade; // ✅
@PostMapping
public ApiResponse<IdResp> create(@Valid @RequestBody PolicyCreateReq req) {
return ApiResponse.ok(new IdResp(facade.create(req)));
}
}
// WRONG
public class PolicyController {
private final PolicyService service; // ❌ 禁止直接注入 Service
}
规则 2: Service 禁止注入其他 Service
这是最常被违反的规则。跨域数据需求必须上推到 Facade 层。
// WRONG - Service 跨域调用
@Service
public class AlgoServiceServiceImpl {
private final LlmServiceMapper llmServiceMapper; // ❌ 注入其他域 Mapper
private final VideoSourceService videoSourceService; // ❌ 注入其他 Service
}
// CORRECT - Facade 编排
@Component
@RequiredArgsConstructor
public class AlgoServiceFacade {
private final AlgoServiceService algoServiceService; // ✅
private final LlmServiceService llmServiceService; // ✅ Facade 注入多个 Service
public AlgoServiceDetail getDetail(Long id) {
AlgoServiceDetail detail = algoServiceService.getDetail(id);
if (detail.getLlmServiceId() != null) {
LlmService llm = llmServiceService.getById(detail.getLlmServiceId());
detail.setLlmServiceName(llm.getName());
}
return detail;
}
}
规则 3: Mapper 保持空接口
查询逻辑在 Service 中使用 LambdaQueryWrapper 构建,保证类型安全。
禁止 在 Mapper 中新增方法或 Mapper XML 自定义 SQL,复杂条件仍在 Service 使用 Wrapper/apply 构建。
// CORRECT
@Mapper
public interface PolicyMapper extends BaseMapper<AlertPolicy> {
// 空接口,不添加任何方法
}
// Service 中构建查询
LambdaQueryWrapper<AlertPolicy> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(AlertPolicy::getEnabled, true)
.orderByDesc(AlertPolicy::getCreateTime);
List<AlertPolicy> list = baseMapper.selectList(wrapper);
// WRONG
@Mapper
public interface PolicyMapper extends BaseMapper<AlertPolicy> {
@Select("SELECT * FROM alert_policy WHERE enabled = 1") // ❌
List<AlertPolicy> findEnabled();
}
事务管理
事务边界
| 场景 | 事务位置 | 示例 | |---|---|---| | 跨服务写操作 | Facade 层 | 创建任务 + 创建子任务 | | 单服务复杂写操作 | Service 层 | 批量删除 + 依赖检查 | | 读操作 | 无事务 | 查询列表、查询详情 | | Controller 层 | 禁止 | — |
事务注解标准写法
@Transactional(rollbackFor = Exception.class)
public Long create(TaskCreateReq req) {
// 业务逻辑
}
事务内禁止
- 远程调用(HTTP / RPC / 消息发送)
- 耗时操作(文件 IO、大量计算)
@Async方法调用
异常处理体系
异常层次
RuntimeException
└── BusinessException (业务异常,含 ErrorCode)
└── AuthConsoleException (外部系统异常)
各层异常职责
| 层 | 职责 |
|---|---|
| Service | 抛出 BusinessException(资源不存在、业务规则违反) |
| Facade | 不捕获 BusinessException,透传给全局处理器;仅捕获需要补偿的外部调用异常 |
| Controller | 不处理异常 |
| GlobalExceptionHandler | 统一捕获,转换为 ApiResponse |
错误码分段管理
按业务模块分配错误码范围(如 2xxx 算法服务、3xxx 预警策略),避免冲突。
日志级别规范
| 场景 | 级别 | 堆栈 | |---|---|---| | 业务异常(BusinessException) | WARN | 无 | | 参数校验失败 | WARN | 无 | | 系统异常(未预期) | ERROR | 有 | | 关键业务操作(创建/更新/删除) | INFO | 无 |
命名规范
Java 命名
| 类型 | 规则 | 示例 |
|---|---|---|
| 类名 | PascalCase | AnalysisTaskService |
| 方法 / 变量 | camelCase | getTaskById, videoSourceId |
| 常量 | UPPER_SNAKE_CASE | MAX_RETRY_COUNT |
| 包名 | 全小写 | com.example.service.impl |
分层命名
| 类型 | 命名模式 |
|---|---|
| Controller | {Resource}Controller |
| Facade | {Resource}Facade (直接类,无接口) |
| Service 接口 | {Resource}Service |
| Service 实现 | {Resource}ServiceImpl |
| Mapper | {Resource}Mapper |
| Entity | {Resource} |
| Converter | {Resource}Converter |
数据库命名
| 类型 | 规则 | 示例 |
|---|---|---|
| 表名 | snake_case | alert_policy, video_source_cache |
| 字段名 | snake_case | create_time, llm_service_id |
| 外键字段 | {关联表}_id | policy_id, task_id |
| 唯一索引 | uk_{fields} | uk_name_version |
| 普通索引 | idx_{fields} | idx_create_time |
API 路径
- 格式:
/api/v{version}/{resources}(复数, kebab-case) - 示例:
/api/v1/llm-services,/api/v1/analysis-tasks
代码质量原则
必须遵守
- 公共 API 方法必须有 Javadoc
- 使用
LambdaQueryWrapper保证类型安全,避免字符串列名 - 不使用物理外键约束,通过应用层保证数据一致性
- 所有表使用逻辑删除(
deleted字段) - 写操作考虑乐观锁(
version_num字段) - 删除前检查依赖关系,防止数据不一致
统一响应格式
所有 API 返回 ApiResponse<T>:
ApiResponse.ok() // 无数据成功
ApiResponse.ok(data) // 带数据成功
ApiResponse.error(code, msg) // 错误
参数校验
- Request Body:
@Valid @RequestBody XxxReq req - Path Variable 校验: 类上加
@Validated,参数上加@NotBlank - 校验注解:
@NotBlank,@NotNull,@NotEmpty(Jakarta Validation)
常见反模式
| 反模式 | 为什么不行 | 正确做法 |
|---|---|---|
| Service 注入其他 Service | 循环依赖、职责混乱、难以测试 | 跨域编排放 Facade 层 |
| Controller 直接调用 Service | 跳过 Facade 编排层,业务逻辑散落 | Controller → Facade → Service |
| Mapper 中写自定义 SQL | 丢失类型安全,SQL 散落 | Service 中用 LambdaQueryWrapper |
| 事务内做 HTTP/RPC 调用 | 长事务、锁争用、调用失败导致回滚 | 事务外调用,或拆分事务 |
| 捕获 BusinessException 后吞掉 | 错误被隐藏,调用方无法感知 | 让全局处理器统一处理 |
| Entity 直接作为 API 响应 | 暴露内部结构,难以演化 | 使用 DTO 做 API 契约 |
| 不指定 rollbackFor | 默认只回滚 RuntimeException | @Transactional(rollbackFor = Exception.class) |
微信扫一扫