前言
后端架构中Controller-Service-DAO三层架构非常常见,因为之前学的C++,没有学Java的Spring Boot等框架,所以还不太了解。现在看了项目里很多代码都是基于这种架构,特地来学习一下。
各层核心职责和作用
这种架构基于单一职责原则设计,核心目标是拆分请求处理,业务逻辑和数据访问的职责。好处是降低代码耦合度,提升可维护性和可测试性。整个三层严格遵循 "单向依赖"(Controller 仅调用 Service,Service 仅调用 DAO,禁止反向或跨层调用)
Controller层
核心职责
接收前端请求,参数校验,调用Service,返回响应
作用
作为前端请求和后端业务逻辑的桥梁,不负责任何业务逻辑,只负责需求转发
价值
- 过滤无效参数,拦截无效请求,从而避免消耗Service层资源,减少不必要业务逻辑代码执行;
- 统一请求入口,减少前后端交互的复杂性
- 统一响应格式,且可以用用户友好度高的方式返回错误处理,避免暴露后端堆栈错误信息或者SQL错误信息等
- 防止xss攻击
Service层
核心职责
封装所有的业务逻辑,协调多个DAO
作用
后端的"大脑",是业务规则的实现地,但不直接操作数据库,而是通过DAO获取/修改数据
价值
- 将业务规则、计算、判断逻辑集中在Service层实现,实现高内聚;业务逻辑的变化之需要修改Service层
- 协调多个资源和多个DAO,可以实现复杂的业务流程
DAO层
核心职责
仅负责与数据库交互,实现数据的 CRUD(增删改查),不包含任何业务逻辑
作用
隔离 "业务逻辑" 与 "数据操作",当数据库(如 MySQL→PostgreSQL)或访问方式(如 JDBC→MyBatis)变化时,仅需修改 DAO 层,不影响 Service 和 Controller。
例子
下面我用 Go 语言实现一个极简的用户登录注册模块,展示 Controller→Service→DAO 三层架构的设计。这个示例包含用户注册和登录两个核心功能,清晰展示了各层的职责划分。
main.go
go
package main
import (
"net/http"
)
func main() {
// 初始化各层实例
userDAO := NewUserDAO()
userService := NewUserService(userDAO)
userController := NewUserController(userService)
// 注册路由
http.HandleFunc("/register", userController.Register)
http.HandleFunc("/login", userController.Login)
// 启动服务
println("Server is running on :8080")
http.ListenAndServe(":8080", nil)
}
Controller层
go
package main
import (
"encoding/json"
"net/http"
"strings"
)
// UserController 处理用户相关的HTTP请求
type UserController struct {
userService *UserService
}
// NewUserController 创建UserController实例
func NewUserController(service *UserService) *UserController {
return &UserController{
userService: service,
}
}
// Register 处理用户注册请求
func (c *UserController) Register(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
// 只允许POST方法
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
json.NewEncoder(w).Encode(map[string]string{"error": "只允许POST方法"})
return
}
// 解析请求参数
var req struct {
Username string `json:"username"`
Password string `json:"password"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{"error": "请求参数格式错误"})
return
}
// 简单的参数校验
if strings.TrimSpace(req.Username) == "" || strings.TrimSpace(req.Password) == "" {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{"error": "用户名和密码不能为空"})
return
}
// 调用Service层处理业务逻辑
user, err := c.userService.Register(req.Username, req.Password)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
return
}
// 返回成功响应
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(map[string]interface{}{
"message": "注册成功",
"user": user,
})
}
// Login 处理用户登录请求
func (c *UserController) Login(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
// 只允许POST方法
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
json.NewEncoder(w).Encode(map[string]string{"error": "只允许POST方法"})
return
}
// 解析请求参数
var req struct {
Username string `json:"username"`
Password string `json:"password"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{"error": "请求参数格式错误"})
return
}
// 调用Service层处理业务逻辑
user, err := c.userService.Login(req.Username, req.Password)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
return
}
// 返回成功响应
json.NewEncoder(w).Encode(map[string]interface{}{
"message": "登录成功",
"user": user,
})
}
Service层
go
package main
import (
"errors"
"golang.org/x/crypto/bcrypt"
)
// UserService 处理用户相关的业务逻辑
type UserService struct {
userDAO *UserDAO
}
// NewUserService 创建UserService实例
func NewUserService(dao *UserDAO) *UserService {
return &UserService{
userDAO: dao,
}
}
// Register 处理用户注册业务逻辑
func (s *UserService) Register(username, password string) (*User, error) {
// 检查用户名是否已存在
existingUser, _ := s.userDAO.FindByUsername(username)
if existingUser != nil {
return nil, errors.New("用户名已存在")
}
// 密码加密
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return nil, errors.New("密码加密失败")
}
// 调用DAO层保存用户
user := &User{
Username: username,
Password: string(hashedPassword),
}
return s.userDAO.Save(user)
}
// Login 处理用户登录业务逻辑
func (s *UserService) Login(username, password string) (*User, error) {
// 调用DAO层查询用户
user, err := s.userDAO.FindByUsername(username)
if err != nil || user == nil {
return nil, errors.New("用户名或密码错误")
}
// 验证密码
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password))
if err != nil {
return nil, errors.New("用户名或密码错误")
}
return user, nil
}
DAO层
go
package main
import (
"errors"
"sync"
)
// UserDAO 处理用户数据的访问操作
type UserDAO struct {
users map[string]*User // 模拟数据库,key为用户名
nextID int
mu sync.Mutex // 保证并发安全
}
// NewUserDAO 创建UserDAO实例
func NewUserDAO() *UserDAO {
return &UserDAO{
users: make(map[string]*User),
nextID: 1,
}
}
// Save 保存用户到数据存储
func (d *UserDAO) Save(user *User) (*User, error) {
d.mu.Lock()
defer d.mu.Unlock()
// 设置ID
user.ID = d.nextID
d.nextID++
// 保存用户
d.users[user.Username] = user
return user, nil
}
// FindByUsername 根据用户名查询用户
func (d *UserDAO) FindByUsername(username string) (*User, error) {
d.mu.Lock()
defer d.mu.Unlock()
user, exists := d.users[username]
if !exists {
return nil, errors.New("用户不存在")
}
return user, nil
}
go
package main
// User 用户模型
type User struct {
ID int `json:"id"`
Username string `json:"username"`
Password string `json:"-"` // 密码不返回给前端
}