🏗️ 后端分层架构

什么是分层架构?

分层架构是一种后端设计模式,其中应用程序分为不同的层,每个层都有特定的职责。

1
Client → Controller → Service → Repository → Database

它确保干净的代码、关注点分离和可扩展性


为什么使用它?

它解决的问题

  • 将业务逻辑与 HTTP 逻辑混合
  • 控制器内的 SQL 查询
  • 难以维护且混乱的代码
  • 测试困难
  • 紧耦合

好处

  • 结构简洁
  • 轻松测试
  • 可扩展的设计
  • 可维护的代码
  • 团队友好

层解释

1️⃣ 控制器层

作用: 处理 HTTP 请求。

职责:

  • 验证输入
  • 验证/授权
  • 致电服务
  • 返回响应

用于: 仅请求级验证。


2️⃣ 服务层

作用: 包含业务逻辑。

职责:

  • 应用业务规则
  • 协调存储库
  • 数据转换

用途: 业务验证(例如,“用户已存在”)。


3️⃣ 存储库层

作用: 数据库交互。

职责:

  • 增删改查操作
  • 与DB / ORM通信

用途: 所有数据库访问。


验证规则

类型
输入验证 控制器
业务验证 服务

何时使用

推荐用于:

  • API
  • 可扩展的后端系统
  • 生产项目
  • 团队环境

不需要:

  • 小脚本
  • 简单的 MVP
  • 单文件应用程序

优点和缺点

优点

  • 干净的分离
  • 可重用逻辑
  • 轻松测试
  • 可扩展

缺点

  • 更多文件
  • 样板稍微多一些
  • 对于小型应用程序来说太过分了

总结

分层架构=
控制器→服务→存储库

每一层都有单一的职责,使后端变得干净、结构化和可扩展。

为什么要验证?

验证可确保传入数据在到达您的业务逻辑或数据库之前是正确、安全且符合预期的。


如果没有验证会发生什么?

示例:

您的数据库期望:

1
age: number

但用户发送:

1
age: "twenty"

结果:

  • 数据库查询失败
  • 应用程序崩溃
  • 服务器返回 500 内部服务器错误
  • 糟糕的用户体验
  • 更难的调试

这意味着错误被发现得太晚了(在服务器内部)。


更好的方法:尽早验证

验证控制器层(入口点)的输入。

如果收到无效数据:

  • 立即停止请求
  • 返回 400 错误请求
  • 发送有用的错误消息

响应示例:

1
2
3
{
"error": "Age must be a number"
}

验证解决什么问题

  • 防止服务器崩溃
  • 保护数据库完整性
  • 提高安全性
  • 改善用户体验
  • 节省服务器资源
  • 使调试更容易

经验法则

尽早验证。
永远不要相信客户的输入。

始终假设:

  • 用户可能发送错误的数据
  • 可以绕过前端验证
  • API 可公开访问

总结

未经验证 → ❌ 500 内部服务器错误
经过验证 → ✅ 400 错误请求(干净且受控)

验证使您的后端安全、稳定且可用于生产

验证类型

验证不仅仅是检查字段是否存在 - 它确保数据在格式、含义、类型和业务条件方面正确。


1️⃣ 语法验证(格式检查)

检查数据是否遵循所需的模式或结构。

它验证什么

  • 电子邮件格式
  • 电话号码格式
  • 网址格式
  • 密码图案
  • 基于正则表达式的规则

示例

电子邮件验证

1
2
3
{
"email": "yadnyeshgmail.com"
}

失败 — 缺少 @ 和域。


电话号码验证

1
2
3
{
"phone": "12345"
}

失败 — 不是有效的国际电话格式。


密码模式

1
2
3
{
"password": "abc123"
}

失败 — 不符合要求(例如,8 个以上字符、1 个特殊符号)。


2️⃣ 语义验证(逻辑/现实世界的含义)

检查数据在逻辑上是否有意义。

它的格式可能正确,但仍然是错误的。

示例

未来的出生日期

1
2
3
{
"date_of_birth": "2030-01-01"
}

失败——出生日期不能是未来的日期。


负产品价格

1
2
3
{
"price": -500
}

失败——价格不能为负。


年龄限制

1
2
3
{
"age": 15
}

失败 — 必须年满 18 岁才能注册。


3️⃣ 类型验证(数据类型检查)

确保变量类型与 API 期望的类型匹配。

示例

预期数字但收到字符串

1
2
3
{
"age": "twenty"
}

失败 — 应为 number,但收到 string


需要数组但收到字符串

1
2
3
{
"tags": "backend"
}

失败 — 应为 Array<string>,但收到 string

正确的:

1
2
3
{
"tags": ["backend", "api"]
}

4️⃣ 复杂/条件验证

根据其他字段值验证字段。

用于现实世界的业务规则。

示例

婚姻状况规则

1
2
3
4
{
"is_married": true,
"partner_name": ""
}

失败 — 如果 is_married 为 true,则需要 partner_name


折扣规则

1
2
3
4
{
"has_discount": true,
"discount_percentage": 0
}

失败 — 如果启用折扣,折扣百分比必须大于 0。


送货地址规则

1
2
3
4
{
"delivery_type": "home",
"address": ""
}

失败 — 送货上门需要提供地址。


总结

类型 检查
句法 格式/图案
语义 现实世界的逻辑
类型 正确的数据类型
有条件 取决于其他领域

最终规则

良好的后端验证检查:

  • 格式
  • 含义
  • 类型
  • 业务条件

强大的验证=安全+稳定+生产就绪的后端。

🔄 转换(清理)

转换(也称为清理)在传入数据到达业务逻辑或数据库之前对其进行准备。

与验证(检查正确性)不同,转换将数据修改为安全且一致的格式

这可以确保:

  • 更干净的数据库记录
  • 一致的比较
  • 更少的意外错误
  • 更好的安全性

为什么要转换数据?

用户输入通常是:

  • 不一致
  • 格式不正确
  • 区分大小写
  • 包含额外空格
  • 类型错误

如果按原样存储,可能会导致:

  • 重复记录
  • 匹配问题
  • 查询错误
  • 安全风险

所以我们要尽早消毒。


常见转换类型

1️⃣ 铸造(类型转换)

在使用数据之前将其转换为正确的类型。

示例:查询参数始终是字符串。

1
GET /users?page=5

已收到:

1
page = "5"  // string

转换:

1
page = 5  // number

没有铸造:

  • 分页可能会中断
  • 算术运算可能会失败

2️⃣ 标准化(标准化)

使数据一致以便存储和比较。

示例:电子邮件不区分大小写。

1
2
3
{
"email": "User@Email.Com "
}

转换:

1
"user@email.com"

步骤:

  • 修剪空间
  • 转换为小写

没有标准化:

  • "User@email.com""user@email.com" 可以被视为不同的用户。

3️⃣ 格式化(重组数据)

保存前调整结构。

示例:电话号码格式。

输入:

1
9876543210

转换:

1
+919876543210

其他例子:

  • 将日期格式化为 ISO 格式 (YYYY-MM-DD)
  • 从用户名中删除特殊字符
  • 存储前对密码进行哈希处理

4️⃣ 修剪和清洁

删除不需要的字符。

例子:

1
2
3
{
"username": " Yadnyesh "
}

转换:

1
"Yadnyesh"

这可以防止不必要的数据库不一致。


重要规则

验证 → 然后转换 → 然后处理

  1. 验证输入
  2. 清理/转换数据
  3. 将干净的数据传递给服务层

总结

行动 目的
验证 检查数据是否正确
转型 使数据干净一致

清理可确保您的后端保持:

  • 可预测的
  • 安全
  • 一致
  • 生产就绪

前端与后端:黄金法则

特色 前端验证 后端验证
主要目标 用户体验(UX) 安全与数据完整性
可靠性 低(可绕过) 高(最终授权)
工具 HTML5、React/Vue 状态 Joi、Zod、类验证器

注意: 切勿依赖前端来确保安全。如果用户通过 PostmancURL 访问您的 API,您的前端检查将被完全绕过。