面向开发者的系统设计:像建筑师一样思考

本文译自「System Design for Developers: Think Like an Architect」,原文链接towardsdev.com/system-desi...,由Saurabh Singh发布于2025年8月25日。

"建筑是一种社会行为,也是人类活动的物质舞台。"------ Spiro Kostof

正如建筑师不会在没有蓝图的情况下开始建造一样,开发者也不应该在没有系统设计的情况下开始编码。然而,许多开发者直接进入实现阶段,却发现自己陷入了技术债务、性能瓶颈和架构噩梦的迷宫之中,而这些本可以通过适当的规划来避免。

系统设计是一门艺术和科学,它定义系统的架构、组件、模块、接口和数据,以满足特定需求。它就像一座屹立百年的房屋和一座因自身重量而倒塌的房屋之间的区别。

建筑师的思维方式:分解复杂问题

分层思考,而非直线思考

想象一下,你正在设计一座摩天大楼。你不会先决定第 47 层的墙壁要刷什么颜色。你会先从地基开始,然后是结构框架,接着是电气和管道系统,最后是室内设计。

软件系统也遵循同样的原则。想象一下可视化页面构建器应用程序------想想 Webflow、Wix 或 Squarespace 等工具,它们允许用户通过拖放界面创建网页,而无需编写代码。这些系统非常复杂,用户可以:

  • 将组件(按钮、图片、文本块)从库拖放到画布上
  • 通过可视化控件自定义属性(颜色、字体、大小)
  • 在构建过程中实时预览页面
  • 将网站直接发布到 Web 上
  • 与团队成员实时协作

架构师无需深入研究拖放功能的实现细节,而是首先思考:

基础层:核心实体有哪些?

页面、组件、模板、用户、项目

结构层:这些实体之间如何关联?

用户创建项目 项目包含页面 页面由组件组成 组件可以保存为模板

系统层:它们如何通信?

  • 用于 CRUD 操作的 RESTful API
  • 用于实时协作的 WebSocket 连接
  • 用于组件更新的事件驱动架构

界面层:用户如何交互?

  • 用于项目管理的仪表盘
  • 用于页面编辑的画布
  • 用于选择的组件库
  • 用于测试的预览模式

正如软件工程师 Grady Booch 曾经说过的:"优秀软件的功能在于化繁为简。" 这种分层方法将极其复杂的内容转化为易于管理的模块。

📝 互动练习:分层架构

**现在就拿起纸笔!**在继续阅读之前,请尝试以下方法:

  1. 画四个水平矩形,并将它们叠放在一起
  2. 从上到下分别标注:界面、系统、结构、基础
  3. 选择你日常使用的任何应用(Netflix、Amazon、WhatsApp)
  4. 在每一层中填入你认为应该填入的内容

Netflix 示例:_

  • 界面:搜索栏、视频播放器、推荐
  • 系统:流媒体服务、推荐引擎、用户身份验证
  • 结构:用户观看电影,电影有评分
  • 基础:用户、电影、评分、订阅

**为什么要画这个?**你的大脑对你手绘内容的记忆力比你刚刚阅读的内容强 6 倍。这张简单的草图会让你立刻理解文章的其余部分。

分解策略

分解复杂问题就像解剖手表。你需要了解每个齿轮、弹簧和机械装置,然后才能构建或修复整个钟表。

功能分解示例:可视化页面构建器

pgsql 复制代码
Page Builder System
├── Authentication & Authorization
│   ├── User registration/login
│   ├── Role-based permissions
│   └── Session management
├── Project Management
│   ├── Project CRUD operations
│   ├── Version control
│   └── Collaboration features
├── Page Editor
│   ├── Canvas rendering engine
│   ├── Component management
│   ├── Drag-and-drop interface
│   └── Real-time preview
├── Component Library
│   ├── Built-in components
│   ├── Custom components
│   └── Template system
└── Export & Publishing
    ├── Static site generation
    ├── Hosting integration
    └── SEO optimization

每个分支都可以独立开发、单独测试,并系统地集成。这种方法遵循 Unix 哲学:"专心做好一件事。"

📝 互动练习:系统分解树

又该画草图了! 这个练习可以训练你的"架构师大脑":

  1. 画一个树形结构,以"页面构建器"为根
  2. 添加 5 个主要分支(思考:哪些是大块?)
  3. 在每个分支下,添加 2-3 个叶子(较小的部分)
  4. 使用简单的方框和线条------无需复杂的图表!

你的绘图可能看起来像:

神奇时刻:当你无法进一步分解一个方框时,这可能就是一个开发人员一周的工作。如果觉得方框太大,就进一步分解它!

可扩展性:今天构建,应对未来的问题

成长型思维

"种一棵树的最佳时机是20年前,其次是现在。"------中国谚语

可扩展性规划就像为房子选择地基。你可能从一栋小屋开始,但如果你计划扩建成一座豪宅,你就需要一个能够支撑未来发展的地基。

考虑三个可扩展性维度:

垂直扩展(向上扩展):就像在建筑物中增加更多楼层

  • 增加现有服务器上的 CPU、RAM 或存储空间
  • 简单但存在物理限制
  • 单点故障

水平扩展(向外扩展):就像在综合体中建造更多建筑物

  • 添加更多服务器以分散负载
  • 更复杂但几乎不受限制
  • 更好的容错能力

功能扩展:就像为不同用途建造专用建筑物

  • 微服务架构
  • 每个服务处理特定功能
  • 独立扩展和部署

可扩展性的红绿灯系统

🟢 绿灯决策(第一天)

  • 简单的单体架构
  • 单一数据库
  • 使用 Redis 进行基本缓存
  • 静态资源的 CDN

🟡 黄灯决策(流量增长)

  • 数据库只读副本
  • 应用服务器集群
  • API 速率限制
  • 监控和报警系统

🔴 红灯决策(高流量)

  • 微服务架构
  • 数据库分片
  • 用于异步处理的消息队列
  • 自动扩展基础设施

对于我们的页面构建器示例,你可以从一个简单的 Python FastAPI 服务器和 PostgreSQL 数据库开始。随着流量的增长,你将引入:

  1. 缓存层:用于会话存储和频繁访问的模板的 Redis
  2. CDN:用于服务组件资源和生成页面的 CloudFront
  3. 数据库优化:用于分析的读取副本,为不同域提供独立的数据库
  4. 服务分离:用于渲染、文件管理和用户管理的专用微服务

📝 互动练习:扩展演进

这张图将成为你的扩展路线图!

  1. 在你的纸上画三列,分别标记为:"第 1 天"、"第 6 个月"、"第 2 年"
  2. 在每一列中,画出你的架构图:
  • 第 1 天:画两个方框(前端、API),下方画一个圆柱体(数据库)
  • 第 6 个月:添加更多方框(Redis 缓存、CDN 云、负载均衡器)
  • 第 2 年:将 API 方框拆分成多个小方框(用户服务、项目服务等)

3. 在各列之间画箭头,展示演进过程

4. 在每列上方画一些简笔画,展示用户数量:100 → 1 万 → 100 万

**从你的图中得出的关键洞察:**注意复杂性是如何逐渐增长的,而不是一下子增长的。这就是真实系统演进的方式!

职责三位一体:数据库、API 和前端

三层架构理念

将 Web 应用程序想象成一家餐厅:

前端(餐厅):客户互动的地方

  • 用户界面和体验
  • 输入验证和格式化
  • 状态管理和路由

API(厨房):魔法发生的地方

  • 业务逻辑处理
  • 数据转换和验证
  • 与外部服务集成

数据库(食品储藏室):食材存储的地方

  • 数据持久化和检索
  • 数据完整性和关系
  • 性能优化

"好的架构不在于结构本身,而在于它所创造的空间。" --- 安藤忠雄

📝 互动练习:餐厅架构

让我们通过一个绘画练习来具体化这一点:

  1. 绘制一个简单的餐厅平面图,其中包含三个区域:
  • 餐厅(顾客就座的地方)
  • 厨房(准备食物的地方)
  • 储藏室(存放食材的地方)

2. 现在绘制箭头表示数据流向:

  • 顾客点餐 → 厨房
  • 厨房索取食材 → 储藏室
  • 厨房送出准备好的食物 → 餐厅
  1. 为每个区域标注相应的 Web 对应项:
  • 餐厅 = 前端
  • 厨房 = API
  • 储藏室 = 数据库

**为什么这样做有效:**现在你的大脑已经对抽象的系统概念有了物理隐喻。每次设计系统时,想象一下这家餐厅的场景!

职责边界

数据库职责

pgsql 复制代码
-- ✅ Good: Database handles data integrity
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE NOT NULL,
    created_at TIMESTAMP DEFAULT NOW()
);
pgsql 复制代码
-- ✅ Good: Database enforces relationships
CREATE TABLE pages (
    id SERIAL PRIMARY KEY,
    project_id INTEGER REFERENCES projects(id) ON DELETE CASCADE,
    title VARCHAR(255) NOT NULL
);

API 职责(Python 和 FastAPI):

python 复制代码
# ✅ Good: API handles business logic
from fastapi import FastAPI, HTTPException, Depends
from pydantic import BaseModel
python 复制代码
app = FastAPI()@app.post("/api/pages")
async def create_page(
    page_data: PageCreateModel,
    current_user: User = Depends(get_current_user)
):
    # Validate user permissions
    if not can_user_edit_project(current_user, page_data.project_id):
        raise HTTPException(status_code=403, detail="Unauthorized")

        # Apply business rules
    page = await create_page_with_defaults(page_data)

    # Return appropriate response
    return {"page": page, "message": "Page created successfully"}

前端职责(Angular):

typescript 复制代码
// ✅ Good: Frontend handles user interaction
@Component({
  selector: 'app-page-editor',
  templateUrl: './page-editor.component.html'
})
export class PageEditorComponent {
  components: Component[] = [];
  isLoading = false;

  constructor(private apiService: ApiService) {}

  // Handle user interactions
  onComponentDrop(component: Component, position: Position): void {
    this.isLoading = true;

    this.apiService.addComponent(component, position)
      .subscribe({
        next: (result) => this.updateComponents(result),
        error: (error) => this.handleError(error),
        complete: () => this.isLoading = false
      });
  }
}

架构模式:伟大系统的基石

模型-视图-控制器 (MVC):经典模式

MVC 就像组织一场戏剧演出:

  • 模型:剧本和故事(数据和业务逻辑)
  • 视图:舞台和演员(用户界面)
  • 控制器:导演(模型和(查看)
coffeescript 复制代码
# Model: Page data and operations
from sqlalchemy import Column, Integer, String, JSON
from sqlalchemy.ext.declarative import declarative_base
pgsql 复制代码
Base = declarative_base()class PageModel(Base):
    __tablename__ = "pages"

    id = Column(Integer, primary_key=True)
    title = Column(String(255), nullable=False)
    components = Column(JSON)
    project_id = Column(Integer, nullable=False)

    async def save(self, db_session):
        db_session.add(self)
        await db_session.commit()
        return self
coffeescript 复制代码
// View: Page rendering (Angular Component)
@Component({
  selector: 'app-page-view',
  template: `
    <div class="page">
      <h1>{{ pageModel.title }}</h1>
      <app-component 
        *ngFor="let component of pageModel.components"
        [componentData]="component">
      </app-component>
    </div>
  `
})
export class PageViewComponent {
  @Input() pageModel: PageModel;
}
typescript 复制代码
// Controller: Coordination logic (Angular Service)
@Injectable({
  providedIn: 'root'
})
export class PageController {
  constructor(private apiService: ApiService) {}

  async updateTitle(pageId: number, newTitle: string): Promise<void> {
    const updatedPage = await this.apiService.updatePage(pageId, { title: newTitle });
    // Emit event to refresh view
    this.pageUpdated.emit(updatedPage);
  }
}

事件驱动架构:神经系统

事件驱动架构就像人类的神经系统------当某个部分发生事件时,其他部分会自动做出反应。

python 复制代码
# Event system for page builder
from typing import Dict, List, Callable
import asyncio
python 复制代码
class EventBus:
    def __init__(self):
        self.listeners: Dict[str, List[Callable]] = {}

        def on(self, event: str, callback: Callable):
        if event not in self.listeners:
            self.listeners[event] = []
        self.listeners[event].append(callback)

        async def emit(self, event: str, data: dict):
        callbacks = self.listeners.get(event, [])
        await asyncio.gather(*[callback(data) for callback in callbacks])# Usage
event_bus = EventBus()# Auto-save feature
@event_bus.on('component:added')
async def auto_save_handler(data):
    await auto_save(data['page_id'])
    await show_save_indicator()# Analytics tracking
@event_bus.on('component:added')
async def analytics_handler(data):
    analytics.track('Component Added', {
        'component_type': data['component']['type'],
        'page_id': data['page_id']
    })

微服务:专业团队

微服务就像一个爵士乐团------每个音乐家(服务)都是各自乐器的专家,但他们共同努力,创造出美妙的音乐。

nestedtext 复制代码
# Docker Compose for Page Builder Microservices
version: '3.8'
services:
  user-service:
    image: pagebuilder/user-service:python
    environment:
      - DATABASE_URL=postgresql://users_db
      - REDIS_URL=redis://redis:6379
    ports:
      - "8001:8000"

      project-service:
    image: pagebuilder/project-service:python
    environment:
      - DATABASE_URL=postgresql://projects_db
    ports:
      - "8002:8000"

      rendering-service:
    image: pagebuilder/rendering-service:python
    environment:
      - REDIS_URL=redis://redis:6379
      - S3_BUCKET=pagebuilder-assets
    ports:
      - "8003:8000"

      frontend:
    image: pagebuilder/angular-frontend
    ports:
      - "4200:80"
    environment:
      - API_GATEWAY_URL=http://api-gateway:8080

      api-gateway:
    image: pagebuilder/api-gateway:python
    environment:
      - USER_SERVICE_URL=http://user-service:8000
      - PROJECT_SERVICE_URL=http://project-service:8000
      - RENDERING_SERVICE_URL=http://rendering-service:8000
    ports:
      - "8080:8000"

何时使用每种模式

在以下情况下使用 MVC

  • 构建传统 Web 应用程序
  • 团队熟悉该模式
  • 需要清晰的关注点分离

在以下情况下使用事件驱动

  • 实时功能很重要
  • 需要多个系统响应变化
  • 需要松散耦合

在以下情况下使用微服务

  • 团队规模 > 10 名开发人员
  • 不同部分的扩展能力不同
  • 独立部署至关重要

正如 Martin Fowler 所言:"傻瓜也能写出计算机能理解的代码。优秀的程序员写出人类能理解的代码。"

📝 互动练习:架构模式比较

创建你的个人架构模式速查表:

  1. 在你的纸上画出三个部分:"MVC"、"事件驱动"、"微服务"
  2. **对于 MVC:**画出三个相连的方框(模型 ↔ 控制器 ↔ 视图)
  • 在下面写上:"适合:传统应用,清晰的分离"

3.对于事件驱动: 画一个中心圆圈,标记为"事件总线",并用箭头向外辐射到多个方框。

  1. 在下面写:"适用于:实时功能,松耦合"

5. 对于微服务: 画 6-8 个独立的小方框,并用虚线连接它们。

  • 在下面写:"适用于:大型团队,独立扩展"

6. 添加决策树: 从问题到模式画箭头:

  • "团队少于 5 人?" → MVC
  • "需要实时功能吗?" → 事件驱动
  • "多个团队?" → 微服务

** 将此图放在手边!** 这是你未来任何项目的架构决策流程图。

绘制真正有用的系统图

视觉传达的艺术

"一图胜千言,但一张好的图表胜过千次会议。" --- 未知

系统图是软件架构的蓝图。它们应该讲述一个故事,而不是制造混乱。

📝 互动练习:图表层次结构

练习三层系统图绘制方法:

第一层 - 上下文(30 秒绘制):

  1. 绘制三个形状:圆形(用户)、矩形(你的系统)、云(外部服务)
  2. 用带标签的箭头连接:"HTTP 请求"、"API 调用"、"数据同步"
  3. 这回答了"我们的系统连接到什么?"

第二层 - 容器(2 分钟绘制):

  1. 将系统矩形分成四个部分:前端、API 网关、服务、数据库
  2. 用箭头显示它们之间的数据流**
  3. 这回答了"主要部分是什么?"

第三级------组件(5 分钟绘制):

  1. 选择一个容器(例如前端)并将其分解为模块
  2. 展示模块如何在该容器内交互
  3. 这将回答"这个组件内部是如何工作的?"

**本练习的强大之处:**只需选择要绘制的图纸,即可在任何细节层面解释任何系统!

图表的层次结构

上下文图:30,000 英尺的视角

inform7 复制代码
[Users] --> [Page Builder System] --> [CDN]
           |
           v
      [Database]

容器图:构建模块

inform7 复制代码
[Web Browser] --> [Load Balancer] --> [Web Application]
                                         |
                                         v
[File Storage] <-- [API Gateway] <-- [Cache Layer]
                        |
                        v
                   [Database Cluster]

组件图:内部结构

mathematica 复制代码
Web Application:
├── Authentication Module
├── Project Management Module
├── Page Editor Module
│   ├── Canvas Component
│   ├── Toolbar Component
│   └── Properties Panel
├── Component Library Module
└── Export Module

有效的图表绘制原则

  1. 从用户旅程开始:每个图表都应该回答"数据如何从用户操作流向结果?"
  2. 使用一致的符号:矩形表示进程,圆柱体表示数据存储,圆形表示外部实体
  3. 清晰地显示关系:箭头应指示数据流方向并进行标记
  4. 分层图表:从高层开始,然后放大到特定区域
  5. 包含不愉快的路径:显示错误处理和故障场景

整合:可视化页面构建器案例研究

让我们为我们的可视化页面构建器设计一个完整的系统架构:

高层架构

coq 复制代码
Frontend (Angular)
├── Editor Canvas Module
├── Component Library Module  
├── Project Dashboard Module
└── Shared Services
scss 复制代码
API Gateway (Python/FastAPI)
├── Authentication Service
├── Project Service
├── Component Service
└── Rendering ServiceData Layer
├── PostgreSQL (structured data)
├── Redis (caching)
└── S3 (file storage)Infrastructure
├── CDN (CloudFront)
├── Load Balancer
└── Auto-scaling Groups

组件添加的数据流

  1. 用户操作:将组件从库拖到画布
  2. 前端:验证位置,发送 API 请求
  3. API 网关:验证请求,路由到组件服务
  4. 组件服务:验证业务规则,更新数据库
  5. 事件总线:发出"component_added"事件
  6. 渲染服务:生成更新后的页面预览
  7. WebSocket:通知其他协作者
  8. 前端:使用新组件更新 UI

📝 互动练习:数据流映射

将这个抽象的流程转化为一个可视化的故事:

  1. 在一个流程中画出 8 个方框(每个方框对应上面的每个步骤)
  2. 用箭头连接它们以显示顺序
  3. 在每个方框上方写出所需时间:"50 毫秒"、"200 毫秒"、"100 毫秒"等。
  4. 在每个方框下方,注明可能出现的问题:"网络超时"、"身份验证失败"、"数据库宕机"
  5. 现在画出第二个版本,展示步骤 4 失败时会发生什么:
  • 用红色虚线画一条"悲伤的路径"箭头
  • 显示返回给用户的错误消息
  • 添加重试逻辑和回退选项

**本练习将教会你:**每个用户操作实际上都是一个复杂的系统编排。绘制箭头可以帮助你在生产环境中发生潜在故障之前发现它们!

额外提示: 为箭头添加颜色代码------绿色代表正常路径,红色代表错误,蓝色代表重试。

可扩展性考虑因素

流量模式

  • 高读取操作(浏览页面)
  • 突发写入操作(编辑会话)
  • 大文件上传(图片、资源)

扩展策略

  • 将渲染后的页面缓存在 CDN 中
  • 使用只读副本进行页面浏览
  • 将大文件处理排队
  • 实现 WebSocket 连接池

要点:适用于任何 Web 应用程序的原则

系统设计的黄金法则

  1. 从简单开始,规划复杂:从单体应用开始,但要针对微服务进行设计
  2. 快速失败,快速学习:从第一天开始构建监控和报警功能
  3. 数据为王:先设计数据模型,其他一切都水到渠成
  4. 安全性不可或缺:在每一层都构建身份验证和授权机制
  5. 性能是关键特性:用户更容易注意到应用程序运行缓慢,而不是功能缺失

架构清单

在编写第一行代码之前编写代码时,请问自己:

  • \] 核心实体及其关系是什么?

  • \] 单点故障点在哪里?

  • \] 出现问题时会发生什么?

  • \] 安全隐患是什么?

创建你的个人系统设计模板,可用于任何项目:

  1. 绘制一个包含以下部分的空白模板:
bash 复制代码
┌────── 实体与关系 ────────┐ ┌───── 流量与扩展 ─────┐  │ │ │ │ │ │ │ 
└────────────────────────┘ └────────────────────┘ ┌─ 故障点 ──────
─────┐ ┌──── 团队边界 ────┐ │ │ │ │ │ │ 
└─────────────────────┘ └──────────────────────┘
  1. 填写我们的页面构建器示例:
  • 实体:用户、项目、页面、组件
  • 流量:浏览时读取量大,编辑时写入量大
  • 故障:数据库宕机、CDN 速度慢、WebSocket 断开连接
  • 团队:前端团队、API 团队、基础设施团队

2. 复制此模板 --- 将其用于未来的每个项目

为什么有效: 拥有一致的思维框架可以防止你忘记关键方面。你的绘图将成为你的系统设计清单!

你的绘图之旅:从草图到系统

如果你完成了本文的绘图练习,那么你现在拥有:

  1. 分层架构模板(用于分解任何复杂系统)
  2. 分解树方法(用于组织开发工作)
  3. 扩展演进路线图(用于规划发展阶段)
  4. 餐厅隐喻图(用于解释三层架构)
  5. 架构模式对比图(用于选择正确的方法)
  6. 三层图表系统(用于在任何细节层面进行沟通)
  7. 数据流映射技术(用于理解系统交互)
  8. 可复用系统设计模板(用于一致的项目规划)

这些手绘图表比任何昂贵的工具都更有价值,因为:

  • 通过绘制它们,你的大脑建立了更深层次的联系
  • 你可以随时随地重新创建它们
  • 它们根据你对系统的理解进行个性化定制
  • 它们弥合了抽象概念与实践之间的差距实施

保留你的图纸! 把它们贴在你的显示器上,用手机拍照,或者创建一个"系统设计速写本",让它随着每个项目的进展而不断更新。

总结

"架构师身处两个世界之间。你必须愿意打破基本的假设。"------安藤忠雄

系统设计并非要创造完美的架构,而是要做出明智的权衡。每个决策都有其后果,每个模式都有其利弊,每个解决方案都会产生新的问题需要解决。

最好的架构师并非精通所有模式和技术,而是能够倾听需求、理解约束,并设计出能够与用户和组织和谐共存的系统。

记住:你不仅仅是在构建软件,你还在为团队未来的成功奠定基础。构建好它,清晰地记录它,并始终做好演进的准备。

俗话说,"几周的编码时间可以节省数小时的规划时间。" 投资于系统设计,未来的你(和团队)会感谢你。

千个应用程序的旅程始于一个精心设计的系统。今天就开始像架构师一样思考,见证你的应用程序从脆弱的原型转变为经得起时间考验的强大、可扩展的平台。

欢迎搜索并关注 公众号「稀有猿诉」 获取更多的优质文章!

保护原创,请勿转载!

相关推荐
阿巴斯甜6 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker7 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95278 小时前
Andorid Google 登录接入文档
android
黄林晴9 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android