WHAT - 从前端组件化思维到后端架构设计入门

文章目录

  • 一、核心概念:什么是后端架构
  • 二、三种常见架构模式
    • [1. 单体架构(Monolith)](#1. 单体架构(Monolith))
    • [2. 分层架构(Layered)](#2. 分层架构(Layered))
    • [3. 微服务架构(Microservices)](#3. 微服务架构(Microservices))
  • [三、代码示例:Go 分层架构最小实现](#三、代码示例:Go 分层架构最小实现)
  • 四、与前端类比对照表
  • 五、思考题:电商系统
    • [1. 什么情况下选模块化单体](#1. 什么情况下选模块化单体)
    • [2. 为什么不建议一开始就拆微服务](#2. 为什么不建议一开始就拆微服务)
    • [3. 什么时候值得拆微服务](#3. 什么时候值得拆微服务)
    • [4. 如果选微服务,怎么拆](#4. 如果选微服务,怎么拆)
      • [1. 用户服务](#1. 用户服务)
      • [2. 商品服务](#2. 商品服务)
      • [3. 库存服务](#3. 库存服务)
      • [4. 订单服务](#4. 订单服务)
      • [5. 支付服务](#5. 支付服务)
    • [5. 一次下单如何跨服务完成](#5. 一次下单如何跨服务完成)
    • [6. 和前端 monorepo 的类比](#6. 和前端 monorepo 的类比)
  • [进阶 架构设计的 3 个核心原则](#[进阶] 架构设计的 3 个核心原则)

从前端组件化思维,到后端分层与服务拆分------今天我们站在更高的视角,看后端系统是怎么"搭积木"的。

一、核心概念:什么是后端架构

前端架构你一定不陌生:组件化、状态管理(Redux/Pinia)、路由、构建工具链......这些都是前端架构的范畴。

后端架构回答的是同一个问题的另一面:一个后端系统由哪些部分组成?它们怎么分工、怎么通信?

从前端视角看,你可以把后端架构理解成:

  • 前端组件 = 后端模块/服务
  • 组件间 props / 事件通信 = 服务间 API / 消息队列通信
  • 前端状态管理 = 后端数据库 + 缓存
  • 前端路由 = 后端 API 网关 + 负载均衡

二、三种常见架构模式

1. 单体架构(Monolith)

所有功能打包在一个应用里,就像你写一个 Vue/React 单页应用,所有组件都在同一个项目里。

  • ✅ 简单、开发快、部署方便
  • ❌ 代码越来越大后,改一处怕影响全局(和前端巨型组件一个道理)

2. 分层架构(Layered)

把单体按"职责"横向切成几层,最经典的是三层架构:

复制代码
┌─────────────────────────┐
│   Controller 层(接口)  │ ← 接收 HTTP 请求,就像前端的"页面组件"
├─────────────────────────┤
│   Service 层(业务逻辑) │ ← 核心业务处理,就像前端的"业务 hooks / store"
├─────────────────────────┤
│   DAO/Repository 层     │ ← 操作数据库,就像前端的"API 请求封装层"
└─────────────────────────┘

这和前端的"页面组件 → 业务逻辑 → API 封装"分层思路几乎一模一样!

3. 微服务架构(Microservices)

把大系统拆成多个小服务,每个服务独立部署、独立数据库,通过 HTTP/RPC/消息队列通信。

类比前端:就像微前端(Micro Frontends)------每个团队维护自己的子应用,最后拼在一起。


三、代码示例:Go 分层架构最小实现

下面是一个典型的三层架构 Go 项目结构示意(5-15 行核心代码):

go 复制代码
// main.go - 入口,组装各层(类比前端 main.ts 里挂载 App)
func main() {
    db := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    repo := repository.NewUserRepository(db) // DAO 层
    svc := service.NewUserService(repo)      // Service 层
    handler := handler.NewUserHandler(svc)   // Controller 层

    r := gin.Default()
    r.GET("/users/:id", handler.GetUser)
    r.Run(":8080")
}
go 复制代码
// handler/user.go - Controller 层:只管收请求、返响应
func (h *UserHandler) GetUser(c *gin.Context) {
    id := c.Param("id")
    user, err := h.service.GetByID(id) // 调 Service 层
    if err != nil {
        c.JSON(404, gin.H{"error": "not found"})
        return
    }
    c.JSON(200, user)
}
go 复制代码
// service/user.go - Service 层:业务逻辑在这里
func (s *UserService) GetByID(id string) (*User, error) {
    if id == "" {
        return nil, errors.New("invalid id") // 业务校验
    }
    return s.repo.FindByID(id) // 调 DAO 层
}

关键原则:每层只依赖下一层,不反向依赖。 就像前端组件只调用 hooks,hooks 只调 API------方向不能乱。


四、与前端类比对照表

后端概念 前端对应物 作用
Controller / Handler 页面组件 / 路由组件 接收输入、返回输出
Service 层 业务 hooks / Pinia/Vuex actions 核心业务逻辑
Repository / DAO API 请求封装层(axios 封装) 数据存取
数据库 后端的"数据源" 持久化存储
缓存(Redis) 前端的 localStorage / 内存缓存 加速访问
消息队列 EventBus / 事件总线 异步解耦
API 网关 前端的 Nginx 反向代理 统一入口、路由分发
微服务 微前端(Micro Frontends) 按业务拆分、独立部署

五、思考题:电商系统

你现在负责一个电商系统的后端设计:用户、商品、订单、支付四个模块。

你会选择单体架构还是微服务架构?为什么?如果选微服务,你会怎么拆?

提示:想想前端什么时候用单仓库(monorepo)、什么时候拆成多个独立项目------背后的权衡逻辑是一样的。

默认选择是:先做模块化单体,不直接上微服务。

原因不是系统只有四个模块,而是架构要匹配当前的团队规模、业务复杂度和运维能力。用户、商品、订单、支付可以有清晰边界,但"有边界"不等于"必须跨网络部署"。

1. 什么情况下选模块化单体

如果目前是这些条件:

  • 一个小团队负责整个系统
  • 产品还在快速试错
  • 用户量和订单量暂时不确定
  • 各模块经常一起修改、一起发布
  • 尚未建设完善的监控、链路追踪、消息队列和自动化部署

建议先使用一个 Go 服务、一个仓库、一个部署单元:

text 复制代码
ecommerce/
├── cmd/
│   └── api/
│       └── main.go
├── internal/
│   ├── user/
│   │   ├── handler.go
│   │   ├── service.go
│   │   └── repository.go
│   ├── product/
│   ├── inventory/
│   ├── order/
│   ├── payment/
│   └── platform/
│       ├── database/
│       ├── cache/
│       └── messaging/
└── migrations/

虽然是单体,但要遵守模块边界:

text 复制代码
订单模块不能直接修改支付表
支付模块不能直接修改订单表
商品模块不能直接读取用户模块的内部对象

模块之间通过明确的接口调用:

go 复制代码
type PaymentService interface {
	CreatePayment(ctx context.Context, orderID string, amount int64) error
}

这样部署简单、事务简单、调试方便,同时保留了将来拆服务的可能。

2. 为什么不建议一开始就拆微服务

微服务把原来的函数调用:

go 复制代码
paymentService.Pay(order)

变成跨网络调用:

text 复制代码
Order Service
    ↓ HTTP/gRPC
Payment Service

随之而来的不只是多个 Go 项目,还包括:

  • 服务发现
  • API 网关
  • 超时和重试
  • 熔断、限流
  • 分布式链路追踪
  • 消息队列
  • 配置中心
  • 多套 CI/CD
  • 数据一致性
  • 接口版本兼容
  • 局部失败处理
  • 分布式事务或补偿机制

微软的架构指南也指出,微服务可以独立部署和扩容,但会引入服务通信、测试、数据一致性和运维等系统级复杂度。Azure 微服务架构指南

所以微服务实际上是:

用系统复杂度,换取团队、部署、扩容和故障隔离方面的独立性。

3. 什么时候值得拆微服务

出现以下信号时,才开始考虑拆分:

信号 说明
多个独立团队 每个团队需要独立开发和发布
发布互相阻塞 改支付代码却必须重新发布整个商城
流量差异明显 商品查询很大,支付流量小但安全要求高
SLA 不同 商品暂时不可用还能降级,支付不能随便失败
安全要求不同 支付密钥、权限、审计需要单独隔离
技术需求不同 搜索可能用 Elasticsearch,订单使用 MySQL
单体边界已经稳定 已经知道哪些业务总是一起变化
运维体系成熟 有监控、告警、追踪、自动部署和故障演练

微服务应该围绕"业务能力"或"限界上下文"拆分,而不是机械地按照表或者代码目录拆分。AWS 按业务子域拆分指南

4. 如果选微服务,怎么拆

电商系统不一定只拆成题目里的四个服务。考虑增加库存边界:

text 复制代码
                    API Gateway
                         │
        ┌────────────────┼────────────────┐
        ↓                ↓                ↓
   User Service    Product Service   Order Service
                         │                │
                         ↓                ↓
                 Inventory Service  Payment Service

1. 用户服务

负责:

  • 注册、登录
  • 用户资料
  • 收货地址
  • 用户状态

拥有自己的数据:

text 复制代码
users
addresses
user_credentials

2. 商品服务

负责:

  • SPU/SKU
  • 商品名称、描述、分类
  • 商品上下架
  • 基础价格

拥有:

text 复制代码
products
skus
categories

复杂促销场景下,可以以后再拆出定价和营销服务,不必一开始就拆。

3. 库存服务

负责:

  • 可售库存
  • 库存预占
  • 确认扣减
  • 释放库存

拥有:

text 复制代码
stocks
stock_reservations
stock_records

库存最好从商品中独立出来,因为商品资料是"读多写少",库存却是高并发修改,而且涉及超卖问题。

简单系统中也可以先把库存保留在商品模块。

4. 订单服务

负责:

  • 创建订单
  • 订单项
  • 订单状态机
  • 取消和关闭订单
  • 保存下单快照

拥有:

text 复制代码
orders
order_items
order_status_logs

订单中应保存商品快照:

text 复制代码
商品名称
SKU
成交价格
购买数量
收货地址

不能每次查看历史订单时都读取商品服务的当前价格,否则商品改价以后,历史订单也会跟着变化。

5. 支付服务

负责:

  • 创建支付单
  • 调用微信、支付宝等渠道
  • 支付回调
  • 退款
  • 对账
  • 支付幂等

拥有:

text 复制代码
payments
payment_attempts
refunds
callback_records

支付服务只负责"钱是否支付成功",订单服务负责"订单应该变成什么状态"。

5. 一次下单如何跨服务完成

不能依赖一个跨五个数据库的大事务,一般使用状态机、消息和补偿机制:

text 复制代码
1. 创建待支付订单
2. 库存服务预占库存
3. 支付服务创建支付单
4. 用户完成支付
5. 支付服务处理回调并发布 PaymentSucceeded
6. 订单服务将订单改成已支付
7. 库存服务确认扣减库存

如果支付超时:

text 复制代码
订单关闭
→ 发布 OrderCancelled
→ 库存服务释放预占库存

这属于 Saga 思路:每个服务完成自己的本地事务,失败时执行对应的补偿操作。

还需要保证:

  • 支付回调幂等
  • 消息可能重复消费
  • 消息发送失败可以重试
  • 状态迁移不能倒退
  • 服务调用必须有超时
  • 不能让多个服务直接共享数据库

6. 和前端 monorepo 的类比

两者背后的判断逻辑确实相似:

text 复制代码
模块经常一起变化
团队规模较小
统一发布更简单
        ↓
倾向 monorepo / 模块化单体
text 复制代码
团队相互独立
发布节奏不同
技术和权限要求不同
需要独立扩容
        ↓
倾向独立项目 / 微服务

不过要注意:

monorepo 是代码仓库边界,微服务是运行和部署边界,两者不是同一个概念。

完全可以:

  • 单体使用 monorepo
  • 多个微服务仍然放在同一个 monorepo
  • 多个微服务分别放在不同仓库

最终建议是:

新项目先做边界清晰的模块化单体;当某个模块产生明确的独立团队、独立发布、独立扩容、安全隔离或故障隔离需求时,再沿着已有模块边界逐步拆出微服务。

对于这个电商系统,通常优先考虑拆出的会是支付服务库存服务:支付有安全与故障隔离需求,库存有高并发和一致性需求。

进阶 架构设计的 3 个核心原则

  1. 单一职责:每个模块/服务只做一件事(和前端组件化一个道理)
  2. 高内聚、低耦合:相关的代码放一起,模块之间依赖越少越好
  3. 面向接口编程 :依赖抽象(接口),不依赖具体实现------就像前端依赖 axios 实例而不是直接写死 fetch

好的后端架构和好的前端架构,底层逻辑完全一致------都是为了可维护、可扩展、好协作

相关推荐
一只鹿鹿鹿2 小时前
面向智能制造的 RPA 业务自动化整体解决方案(PPT)
大数据·安全·架构·系统安全·制造
是立不是利3 小时前
大文件导致请求被拒的连锁反应
开发语言·前端·javascript
志尊宝3 小时前
Vue3 零基础每日笔记(026):emit 子传父——defineEmits 与事件校验
前端·javascript·html·vue3
风骏时光牛马3 小时前
6个高频实用技术脚本案例,覆盖运维与办公自动化(可直接复用)
前端
爱吃红星柚3 小时前
【学习】Elpis 抽离与发布 npm 包
前端·前端框架·node.js
志栋智能3 小时前
超自动化巡检如何生成“有灵魂”的运维报告?
大数据·运维·人工智能·架构·自动化
PedroQue994 小时前
0.5.0重磅发布:动画插件+H5体验全面优化
前端·uni-app
金虹桥程序员4 小时前
【动画】 📚 CSS 动画与 GPU 合成层优化全链路指南
前端·面试
程序员老赵4 小时前
Docker 部署填鸭表单完整教程:搭建私有化问卷与表单收集平台
前端·docker·开源