第 3 章:架构设计
学习目标
- 掌握前后端分离项目的分层架构
- 设计模块依赖关系与包结构
- 熟悉常见的部署架构模式
一、整体架构
TaskFlow 采用经典前后端分离 + 三层架构。前端 Vue 单页应用通过 Nginx 反向代理访问后端 API,后端按 Controller / Service / Mapper 分层。
┌──────────────────────────────────────────┐
│ 浏览器 (Chrome / Edge) │
└─────────────────┬────────────────────────┘
│ HTTP/HTTPS
┌─────────────────▼────────────────────────┐
│ Nginx (反向代理 + 静态资源) │
└─────────────────┬────────────────────────┘
│
┌─────────────────▼────────────────────────┐
│ Spring Boot 应用 (Tomcat 嵌入) │
│ ┌────────────────────────────────────┐ │
│ │ Controller (接收请求) │ │
│ ├────────────────────────────────────┤ │
│ │ Service (业务逻辑) │ │
│ ├────────────────────────────────────┤ │
│ │ Mapper (数据访问) │ │
│ └────────────────────────────────────┘ │
└────┬──────────────┬──────────────┬───────┘
│ │ │
┌──▼───┐ ┌──▼───┐ ┌───▼───┐
│MySQL │ │Redis │ │RabbitMQ│
└──────┘ └──────┘ └───────┘二、后端分层
java
// 标准的 Controller / Service / Mapper 三层
@RestController
@RequestMapping("/api/user")
@RequiredArgsConstructor
public class UserController { // ① 控制层:接收请求、参数校验、返回响应
private final UserService userService;
@GetMapping("/{id}")
public Result<UserVO> getById(@PathVariable Long id) {
return Result.ok(userService.getVOById(id));
}
}
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService { // ② 业务层:事务、权限、业务编排
private final UserMapper userMapper;
@Transactional
@Override
public UserVO getVOById(Long id) {
User user = userMapper.selectById(id);
Assert.notNull(user, ErrorCode.USER_NOT_FOUND);
return convertToVO(user);
}
}
@Mapper
public interface UserMapper extends BaseMapper<User> { // ③ 数据层:SQL 操作
User selectByUsername(@Param("username") String username);
}各层职责:
- Controller:接请求、验参数、调 Service、包响应、不写业务逻辑
- Service:事务控制、业务编排、跨 Mapper 操作、权限校验
- Mapper:SQL 操作,继承 MyBatis Plus 的
BaseMapper即可省掉 80% 单表 CRUD
⚠️ 坑 1:Controller 里写业务判断是常见坏味道。
if (user.status == 0) ...应该在 Service 层。Controller 只做参数转对象,Service 才决定"能不能继续"。
三、模块依赖关系
taskflow-parent (父 POM,统一版本)
│
├── taskflow-common ← 通用类,不依赖任何业务
│ ↑
├── taskflow-system ← 业务模块,依赖 common
│ ↑
└── taskflow-application ← 启动模块,依赖 system + common父 POM 统一管理版本:
xml
<!-- taskflow-parent/pom.xml -->
<groupId>com.taskflow</groupId>
<artifactId>taskflow-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>taskflow-common</module>
<module>taskflow-system</module>
<module>taskflow-application</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.5</version>
</dependency>
</dependencies>
</dependencyManagement>子模块只写 groupId + artifactId,不写版本号:
xml
<!-- taskflow-system/pom.xml -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<!-- 版本由父 POM 管理 -->
</dependency>
<dependency>
<groupId>com.taskflow</groupId>
<artifactId>taskflow-common</artifactId>
<version>${project.version}</version>
</dependency>四、请求处理流程
一个 HTTP 请求的完整生命周期:
客户端请求
│
▼
① Nginx (反向代理,gzip,SSL)
│
▼
② Tomcat (Servlet 容器)
│
▼
③ Spring Security Filter Chain
- TraceIdFilter (生成请求 ID)
- JwtAuthenticationFilter (验证 token)
- AccessDeniedHandler (权限不足)
│
▼
④ DispatcherServlet (路由匹配)
│
▼
⑤ @RestController 方法 (参数绑定 + @Valid 校验)
│
▼
⑥ @RequiresPermission AOP (权限校验)
│
▼
⑦ Service (业务逻辑,@Transactional 事务)
│
▼
⑧ MyBatis Mapper (SQL 执行)
│
▼
⑨ MySQL / Redis (数据)
│
▼
响应序列化 + 包装 Result<T>五、包结构规范
每个业务模块按职责分包,统一约定:
com.taskflow.system
├── controller/ # 控制层
├── service/ # 业务接口
│ └── impl/ # 业务实现
├── mapper/ # 数据访问
├── entity/ # 数据库实体
├── dto/ # 入参对象(前端传过来)
├── vo/ # 出参对象(返回给前端)
├── convert/ # DTO/VO/Entity 互转
├── constant/ # 常量
└── enums/ # 枚举为什么不用 Entity 直接当 VO?
- Entity 字段多,可能含密码、版本号
- VO 字段是给前端看的,要做脱敏处理
- DTO 是入参校验,加
@NotBlank等注解
java
// 入参 DTO
@Data
public class UserCreateDTO {
@NotBlank
@Length(min = 3, max = 50)
private String username;
@NotBlank
@Length(min = 6, max = 100)
private String password;
}
// 出参 VO(隐藏敏感字段)
@Data
public class UserVO {
private Long id;
private String username;
private String nickname;
// 注意:没有 password 字段!
}六、缓存策略
权限校验每个接口都会触发,必须缓存:
java
@Service
@RequiredArgsConstructor
public class PermissionService {
private final RedisTemplate<String, Object> redis;
private static final String PERM_KEY_PREFIX = "user:perms:";
private static final Duration CACHE_TTL = Duration.ofMinutes(30);
public Set<String> getUserPermissions(Long userId) {
String key = PERM_KEY_PREFIX + userId;
// ① 查缓存
Object cached = redis.opsForValue().get(key);
if (cached instanceof Set<?> set) {
return set.stream().map(Object::toString).collect(Collectors.toSet());
}
// ② 查 DB
Set<String> perms = loadFromDB(userId);
// ③ 写缓存
redis.opsForValue().set(key, perms, CACHE_TTL);
return perms;
}
}缓存失效时机:用户角色变更、菜单权限调整时,主动清理:
java
public void clearCache(Long userId) {
redis.delete(PERM_KEY_PREFIX + userId);
}七、异常处理
统一异常处理用 @RestControllerAdvice 拦截所有 Controller 抛出的异常:
java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public Result<Void> handleBusiness(BusinessException ex) {
return Result.fail(ex.getCode(), ex.getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<Void> handleValid(MethodArgumentNotValidException ex) {
String msg = ex.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining("; "));
return Result.fail(400, msg);
}
}八、本章小结
| 要点 | 关键 |
|---|---|
| 架构模式 | 前后端分离 + 三层架构 |
| 后端分层 | Controller / Service / Mapper |
| 模块依赖 | parent / common / system / application |
| 包结构 | controller / service / mapper / dto / vo |
| 缓存 | Redis 缓存用户权限,30 分钟 TTL |
| 异常 | 全局 @RestControllerAdvice 统一捕获 |
动手练习
- 画架构图:用 ASCII 或 draw.io 画出你项目的后端分层
- 分模块:把现有项目按 common/system/application 拆三个 Maven 模块
- 写包结构:在 system 模块下建 controller/service/mapper/dto/vo 等空包
下一章:第 4 章:数据库设计 →