本文目录导读:
这是一个非常经典且重要的问题,全栈项目的分层架构设计,核心目标是实现高内聚、低耦合,让代码易于维护、测试和扩展。
下面我将以目前最主流的前后端分离模式(前端 SPA + 后端 API)为例,详细阐述一种被广泛验证的分层架构设计。
核心原则
- 单一职责:每一层只负责一个特定的功能领域。
- 依赖倒置:高层模块(如业务逻辑)不应依赖低层模块(如数据库),两者都应依赖抽象(如接口)。
- 接口隔离:层与层之间通过明确的接口(API、DTO、Interface)进行通信。
典型的三层至四层架构(后端视角)
这是最经典的分层方式,根据项目复杂度,可以拆分为更细的层。
展示层 (Presentation Layer / Controller)
- 职责:处理用户界面(Web、移动端、CLI)的请求和响应,它是系统的入口和出口。
- 核心工作:
- 接收HTTP请求,解析参数(Query、Path、Body)。
- 调用业务逻辑层(Service)的方法。
- 将业务逻辑层返回的结果转换成前端需要的格式(通常是JSON)。
- 处理全局异常,返回统一格式的错误响应。
- 不包含任何业务逻辑或数据访问逻辑。
- 技术示例:
UserController.java,@RestController,req/,res/目录(用于定义请求/响应对象)。
业务逻辑层 (Business Logic Layer / Service)
- 职责:系统的核心,处理所有业务规则和工作流,这是项目中“大脑”所在的位置。
- 核心工作:
- 校验用户输入的业务规则(用户名唯一性、余额是否足够、订单状态流转)。
- 编排多个底层服务或数据访问对象(DAO)来完成一个完整的业务用例(创建订单需要同时扣减库存、生成支付记录、发送通知)。
- 处理事务(ACID)。
- 不包含HTTP API 的具体实现细节(如
@GetMapping),也不直接操作数据库。
- 技术示例:
UserService.java,OrderService.java,CustomerService.java。
数据访问层 (Data Access Layer / Repository / DAO)
- 职责:封装与数据源(数据库、缓存、搜索引擎、外部API)的所有交互。
- 核心工作:
- 执行CRUD(增删改查)操作。
- 将数据库查询结果映射为业务对象(如
UserEntity,OrderDO)。 - 管理数据库连接、SQL语句、ORM映射(如 JPA, MyBatis)。
- 注意:这一层的接口应面向业务(如
findByUsername(String username)),而不是面向数据库(如selectById(Long id))。
- 技术示例:
UserRepository.java,UserMapper.java,RedisCache.java。
领域层 (Domain Layer / Model) - 可选但非常推荐
- 职责:存放核心的业务对象和业务规则(实体、值对象、领域服务),这个层是“业务逻辑层”的基石。
- 核心工作:
- 定义 Entity(实体):如
User,Order,可以包含简单的业务方法(如order.cancel())。 - 定义 Value Object(值对象):如
Money,Address,Email。 - 定义 Domain Event(领域事件):如
OrderCreatedEvent。
- 定义 Entity(实体):如
- 为什么推荐:将核心实体和其行为从Service层剥离出来,避免了面向过程的Service(即一个巨大Service类里塞满各种if-else),让业务逻辑更清晰、可测试性更强。
- 与其它层的关系:Service层创建、获取、修改领域对象;Repository层负责持久化或读取领域对象。
项目目录结构示例(以 Java Spring Boot + TypeScript React 为例)
这个结构体现了上述分层思想。
后端 (Java/Spring Boot)
src/main/java/com/youcompany/yourproject/
├── YourApplication.java
├── controller/ // 展示层
│ ├── UserController.java
│ └── request/
│ └── CreateUserRequest.java
│ └── response/
│ └── UserResponse.java
├── service/ // 业务逻辑层
│ ├── UserService.java
│ └── impl/
│ └── UserServiceImpl.java
├── domain/ // 领域层 (核心模型)
│ ├── model/
│ │ ├── User.java // Entity
│ │ ├── Money.java // Value Object
│ │ └── eunms/
│ │ └── UserStatus.java
│ └── service/ // 领域服务 (跨实体的业务逻辑)
│ └── UserDomainService.java
├── repository/ // 数据访问层
│ ├── UserRepository.java // 接口,定义数据库操作
│ └── impl/
│ └── UserRepositoryImpl.java // 使用 JPA/MyBatis 实现
├── infrastructure/ // 基础设施 (可选,与外部系统的交互)
│ ├── config/ // 配置
│ ├── security/ // 安全
│ └── cache/ // 缓存实现
└── shared/ // 通用工具、常量、异常
├── exception/
└── util/
前端 (React/TypeScript)
src/
├── api/ // 网络请求层 (对应后端的Controller)
│ ├── user.ts // 定义调用后端API的函数 (如 getUser, createUser)
│ └── types.ts // 定义请求/响应DTO接口
├── store/ // 状态管理层 (Redux/Zustand/Context)
│ ├── userStore.ts
│ └── types.ts
├── model/ // 领域模型 (对应后端的Domain)
│ ├── User.ts // 前端用的User接口/类
│ └── enums/
│ └── UserStatus.ts
├── service/ // 业务逻辑层 (可选,复杂逻辑时才需要)
│ └── userService.ts // 组合API调用、状态更新、数据转换
├── components/ // 展示层
│ ├── common/ // 通用组件
│ └── features/ // 功能组件
│ ├── UserList/
│ └── UserDetail/
└── pages/ // 页面 (路由组件)
├── HomePage.tsx
└── UserPage.tsx
层间通信与数据流 (以创建用户为例)
- 前端:用户在页面填写
CreateUserForm-> 点击提交 -> 调用api/user.ts的createUser()函数 -> 发送HTTP POST请求到/api/users。 - 后端:
- Controller:
UserController.createUser(CreateUserRequest req)接收请求,验证请求参数格式(非空、类型正确)。 - Service:调用
UserService.createUser(CreateUserDTO dto)。- Service 层执行业务逻辑:检查用户名是否已存在 -> 调用
UserRepository.findByUsername()。 - 如果已存在,抛出
BusinessException。 - 如果不存在,创建
User领域对象。 - Service 层调用
UserRepository.save(user)。
- Service 层执行业务逻辑:检查用户名是否已存在 -> 调用
- Repository:
UserRepositoryImpl.save(User user)-> 使用 JPAsave()方法 -> 保存到数据库。 - Controller:收到 Service 返回的
User对象 -> 调用UserResponse.from(user)转为UserResponseDTO -> 返回HTTP 201 JSON响应。
- Controller:
- 前端:
api/user.ts收到响应 -> 解析JSON ->store/userStore.ts更新状态users.push(newUser)-> React组件自动重新渲染,展示新用户。
关键设计要点与最佳实践
-
DTO (数据传输对象):层与层之间传递的对象。
CreateUserRequest(Controller 接收):{username, email, password}UserResponse(Controller 返回):{id, username, email, createTime}(通常不返回敏感字段如密码)UserDTO(Service 内部使用):可以包含更多业务属性,{username, email, encodedPassword, status}。- 原则:不要将内部 Entity 直接暴露给 Controller 或前端,这样可以隐藏内部实现细节,避免因数据库字段变化导致前端掉线。
-
依赖注入 (DI):各层之间通过接口依赖,而不是具体实现类。
UserService依赖UserRepository接口,而不是UserRepositoryImpl类,这样方便测试(Mock)和更换实现(例如从MySQL切换到PostgreSQL)。
-
异常处理:
- 定义业务异常:
BusinessException(ErrorCode code, String message)。 - 全局异常处理器:在Controller层捕获所有未处理的异常,返回统一的错误JSON格式(如
{code: 1001, message: "用户名已存在"})。不要在每一个Controller方法里都写try-catch。
- 定义业务异常:
-
事务管理:
- 事务边界通常设置在 Service层(一个
@Transactional注解),一个业务用例对应一个事务,确保ACID。
- 事务边界通常设置在 Service层(一个
-
测试:
- 单元测试:重点测试Service层(业务逻辑)和Domain层(实体方法),通过Mock Repository来隔离外部依赖。
- 集成测试:测试Controller + Service + Repository 的完整链路(使用内嵌数据库如 H2)。
- 前端测试:测试Service(API调用)和Store(状态管理),使用Mock API。
一个好的全栈分层架构不是一蹴而就的,需要根据项目规模和团队情况进行权衡。
| 项目类型 | 建议架构模式 | 关键考量 |
|---|---|---|
| 小型/原型项目 | 简约的三层:Controller -> Service -> DAO | 快速迭代,不必过度设计。 |
| 中型/核心业务 | 推荐的四层:Controller -> Service -> Domain -> Repository | 清晰的业务边界,易于测试和维护。 |
| 大型/复杂/团队多 | 领域驱动设计 (DDD) + 微服务 | 解决复杂业务耦合,提升团队并行开发效率。 |
核心思想:让每一层只做它该做的事,Controller 只关心网络,Domain 只关心业务,Repository 只关心存储。 这样你的代码才能像乐高积木一样,灵活组装,易于修改。
标签: 项目分层