分层架构设计
概述
分层架构是一种将应用程序按职责划分为多个水平层次的设计模式。本规范定义了基于 Go、Echo 和 GORM 的三层架构模式,适用于中小型 Web 应用的后端开发。
架构分层
分层架构图
分层职责
| 层级 | 组件 | 职责 |
|---|---|---|
| 表示层 | Handler | 接收 HTTP 请求、参数校验、调用 Service、返回响应 |
| 业务层 | Service | 业务逻辑实现、事务管理、调用 GORM 操作数据 |
| 数据层 | GORM | ORM 映射、数据库操作封装 |
| 基础设施层 | Config/Logger/Tracing | 配置管理、日志、可观测性等横切关注点 |
核心设计原则
原则 1:单一职责
每一层只负责其职责范围内的工作,不跨越层级边界。
- ✅ Handler 只处理 HTTP 请求和响应,不包含业务逻辑
- ✅ Service 只实现业务逻辑,不直接处理 HTTP 请求细节
- ✅ GORM 只负责数据访问,不包含业务规则
原则 2:单向依赖
上层依赖下层,下层不依赖上层。
Handler → Service → GORM → Database
(不允许反向依赖)- ✅ Handler 可以调用 Service
- ✅ Service 可以调用 GORM
- ❌ Service 禁止调用 Handler
- ❌ GORM 禁止调用 Service
原则 3:接口隔离
Handler 通过明确定义的接口与 Service 交互。
- Handler 使用 Service 提供的公共方法
- Service 返回业务数据或错误,不返回 HTTP 状态码
- 跨 Service 调用通过依赖注入实现
详细规范
Handler 层规范
职责
Handler 层负责以下工作:
- 接收和解析 HTTP 请求(参数绑定)
- 校验请求参数格式
- 调用 Service 层方法
- 封装 Service 返回的数据为 HTTP 响应
- 处理 HTTP 状态码和错误响应
禁止事项
Handler 层禁止执行以下操作:
- ❌ 禁止直接调用 GORM 操作数据库
- ❌ 禁止实现业务逻辑(如计算、数据转换、业务规则判断)
- ❌ 禁止直接操作
*gorm.DB实例
代码示例
正确示例 ✅
go
// Handler 只负责请求处理和响应封装
func (h *UserHandler) Create(c echo.Context) error {
// 1. 参数绑定
req := new(CreateUserRequest)
if err := c.Bind(req); err != nil {
return c.JSON(http.StatusBadRequest, response.Fail(1000, "参数格式错误"))
}
// 2. 参数校验
if err := c.Validate(req); err != nil {
return c.JSON(http.StatusBadRequest, response.Fail(1001, err.Error()))
}
// 3. 调用 Service(不包含业务逻辑)
user, err := h.userService.Create(c.Request().Context(), req.Name, req.Email, req.Password)
if err != nil {
return c.JSON(http.StatusInternalServerError, response.Fail(5000, "创建用户失败"))
}
// 4. 返回响应
return c.JSON(http.StatusCreated, response.Success(user))
}错误示例 ❌
go
// 错误:Handler 直接操作数据库
func (h *UserHandler) Create(c echo.Context) error {
req := new(CreateUserRequest)
c.Bind(req)
// ❌ 错误:Handler 直接使用 GORM
user := &models.User{
Name: req.Name,
Email: req.Email,
}
if err := h.db.Create(user).Error; err != nil {
return c.JSON(500, map[string]string{"error": err.Error()})
}
return c.JSON(200, user)
}go
// 错误:Handler 包含业务逻辑
func (h *ProductHandler) UpdatePrice(c echo.Context) error {
req := new(UpdatePriceRequest)
c.Bind(req)
// ❌ 错误:价格计算属于业务逻辑,应在 Service 层
discount := 0.1
if req.IsVIP {
discount = 0.2
}
finalPrice := req.Price * (1 - discount)
// ...
}Service 层规范
职责
Service 层负责以下工作:
- 实现核心业务逻辑
- 管理数据库事务
- 协调多个数据模型的操作
- 调用 GORM 进行数据访问
- 返回业务数据或业务错误
禁止事项
Service 层禁止执行以下操作:
- ❌ 禁止直接访问 HTTP 请求对象(如
echo.Context) - ❌ 禁止返回 HTTP 状态码
- ❌ 禁止处理 HTTP 响应格式
代码示例
正确示例 ✅
go
type UserService struct {
db *gorm.DB
config *config.Config
}
// Service 方法只接受业务参数,返回业务数据或错误
func (s *UserService) Create(ctx context.Context, name, email, password string) (*models.User, error) {
// 1. 业务参数校验
if name == "" || email == "" || password == "" {
return nil, errors.New("参数不完整")
}
// 2. 业务逻辑处理
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return nil, fmt.Errorf("密码加密失败: %w", err)
}
// 3. 构造数据模型
user := &models.User{
Name: name,
Email: email,
Password: string(hashedPassword),
IsActive: true,
}
// 4. 数据库操作
if err := s.db.WithContext(ctx).Create(user).Error; err != nil {
return nil, fmt.Errorf("创建用户失败: %w", err)
}
return user, nil
}事务管理示例 ✅
go
// Service 层管理事务
func (s *OrderService) CreateOrder(ctx context.Context, userID uint, items []OrderItem) (*models.Order, error) {
var order *models.Order
// 使用事务确保数据一致性
err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
// 1. 创建订单
order = &models.Order{
UserID: userID,
Status: "pending",
}
if err := tx.Create(order).Error; err != nil {
return err
}
// 2. 创建订单项
for _, item := range items {
orderItem := &models.OrderItem{
OrderID: order.ID,
ProductID: item.ProductID,
Quantity: item.Quantity,
}
if err := tx.Create(orderItem).Error; err != nil {
return err
}
}
// 3. 更新库存(业务逻辑)
for _, item := range items {
if err := tx.Model(&models.Product{}).
Where("id = ?", item.ProductID).
Update("stock", gorm.Expr("stock - ?", item.Quantity)).
Error; err != nil {
return err
}
}
return nil
})
if err != nil {
return nil, err
}
return order, nil
}错误示例 ❌
go
// 错误:Service 访问 HTTP 请求对象
func (s *UserService) Create(c echo.Context) error { // ❌ 不应接受 echo.Context
// ...
return c.JSON(200, user) // ❌ Service 不应处理 HTTP 响应
}数据层(GORM)规范
职责
GORM 层负责以下工作:
- 执行 SQL 查询和数据操作
- 提供 ORM 映射功能
- 处理数据库连接和查询优化
使用方式
Service 层直接使用 GORM 进行数据操作:
go
// Service 直接调用 GORM 方法
func (s *UserService) GetByID(ctx context.Context, id uint) (*models.User, error) {
var user models.User
if err := s.db.WithContext(ctx).First(&user, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("用户不存在")
}
return nil, err
}
return &user, nil
}注意事项:
- 必须使用
WithContext(ctx)传递上下文 - 应该处理
gorm.ErrRecordNotFound等特定错误 - 避免在 Handler 层直接使用
*gorm.DB
调用规范
调用链路
标准调用链路:
HTTP请求 → Handler → Service → GORM → Database调用约束
Handler → Service
- Handler 必须调用 Service 方法
- Handler 禁止跳过 Service 直接调用 GORM
Service → GORM
- Service 直接使用 GORM 操作数据库
- Service 禁止调用 Handler
跨 Service 调用
- 通过依赖注入实现
- 在 Service 构造函数中注入其他 Service
- 详见 依赖注入规范
跨 Service 调用示例
go
type OrderService struct {
db *gorm.DB
productService *ProductService // 注入其他 Service
userService *UserService
}
func NewOrderService(db *gorm.DB, productService *ProductService, userService *UserService) *OrderService {
return &OrderService{
db: db,
productService: productService,
userService: userService,
}
}
func (s *OrderService) Create(ctx context.Context, userID uint, productID uint) (*models.Order, error) {
// 1. 调用 UserService 验证用户
user, err := s.userService.GetByID(ctx, userID)
if err != nil {
return nil, fmt.Errorf("用户验证失败: %w", err)
}
// 2. 调用 ProductService 检查库存
product, err := s.productService.GetByID(ctx, productID)
if err != nil {
return nil, fmt.Errorf("商品查询失败: %w", err)
}
if product.Stock <= 0 {
return nil, errors.New("商品库存不足")
}
// 3. 创建订单(本 Service 的核心逻辑)
order := &models.Order{
UserID: user.ID,
ProductID: product.ID,
Amount: product.Price,
}
if err := s.db.WithContext(ctx).Create(order).Error; err != nil {
return nil, err
}
return order, nil
}数据流转
请求流转
1. HTTP 请求到达 Handler
↓
2. Handler 绑定和校验参数
↓
3. Handler 调用 Service 方法
↓
4. Service 执行业务逻辑
↓
5. Service 调用 GORM 操作数据库
↓
6. GORM 执行 SQL 并返回结果
↓
7. Service 处理结果并返回给 Handler
↓
8. Handler 封装响应并返回 HTTP 响应数据转换
遵循 DTO 模式进行数据转换:
- Handler 绑定结构体 → Handler 接收 HTTP 请求数据
- Service 请求结构体 → Service 方法参数
- 数据库模型 → GORM 操作的数据结构
- 响应结构体 → Handler 返回给客户端
详见 DTO 分离模式规范
目录组织
推荐的目录结构:
backend/
├── main.go # 应用入口
├── httpapi/ # Handler 层
│ ├── handlers/ # 各业务 Handler
│ │ ├── user.go
│ │ ├── product.go
│ │ └── order.go
│ ├── middleware/ # 中间件
│ ├── response/ # 统一响应体
│ └── router.go # 路由注册
├── services/ # Service 层
│ ├── user.go
│ ├── product.go
│ └── order.go
├── models/ # 数据模型(GORM)
│ ├── user.go
│ ├── product.go
│ └── order.go
└── app/ # 依赖注入容器
└── app.go优势与权衡
优势
- 职责清晰:每层职责明确,易于理解和维护
- 易于测试:Service 层可独立测试,不依赖 HTTP 框架
- 可复用性:Service 层可被多个 Handler 或其他入口复用
- 易于扩展:新增功能只需在对应层添加代码
适用场景
适用于:
- ✅ 中小型 Web 应用
- ✅ CRUD 为主的业务系统
- ✅ 团队熟悉分层架构模式
不适用于:
- ❌ 超大型复杂系统(建议使用 DDD)
- ❌ 微服务架构(每个服务可独立设计)
相关规范
- DTO 分离模式 - 参数绑定与业务实体分离
- 依赖注入规范 - Service 依赖管理
- Service 层设计 - Service 详细规范
- 统一响应体 - Handler 响应格式
- Echo 框架规范 - Echo 使用规范
- GORM 使用规范 - GORM 最佳实践
检查清单
实施分层架构时,确保以下要点:
- [ ] Handler 只调用 Service,不直接操作 GORM
- [ ] Service 包含所有业务逻辑,Handler 不包含业务逻辑
- [ ] Service 方法不接受
echo.Context参数 - [ ] Service 方法返回业务数据或错误,不返回 HTTP 状态码
- [ ] 跨 Service 调用通过依赖注入实现
- [ ] 事务管理在 Service 层实现
- [ ] Handler 使用统一响应体格式