FastAPI: 项目设计 — PriceTracker 全栈架构蓝图

最后更新:2026-08-26

项目设计就像盖楼前的蓝图——没有蓝图的施工,盖到三层发现地基不够,只能推倒重来。好蓝图不是画完就扔的,而是施工全程的导航。

1. 你将学到


2. Alice 的真实故事

(1) 痛点:功能越加越乱

PriceTracker 从一个简单 API 演变成功能越来越多的服务,但没有统一设计——商品和价格的边界模糊,订阅逻辑散落在 10 个端点,通知系统与业务逻辑耦合。每次加功能都要改 5 个文件,回归 Bug 频发。

(2) 领域驱动设计的解法

领域驱动设计(DDD)从业务视角识别核心领域和边界:商品聚合、价格聚合、用户聚合、订阅聚合——每个聚合有清晰的边界和职责,跨聚合通过事件通信,不再互相纠缠。

(3) 收益

新功能只需修改对应聚合内的代码,不再牵一发动全身。订阅逻辑集中在 Subscription 聚合,通知通过 PriceUpdated 事件触发,解耦彻底。


3. 需求分析

(1) 核心用例

角色 用例 优先级
用户(Alice) 注册/登录 P0
用户 查询商品价格 P0
用户 订阅价格变动通知 P1
管理员 批量导入价格 P0
管理员 管理商品 CRUD P0
前端(Bob) 调用 API 获取数据 P0
DevOps(Charlie) 监控系统健康 P1
系统 自动爬取价格 P2

▶ 示例:用例优先级排序脚本

PYTHON
# scripts/prioritize_use_cases.py
use_cases = [
    {"role": "User", "action": "register_login", "priority": "P0", "effort": 2},
    {"role": "User", "action": "query_price", "priority": "P0", "effort": 1},
    {"role": "User", "action": "subscribe_alert", "priority": "P1", "effort": 3},
    {"role": "Admin", "action": "bulk_import", "priority": "P0", "effort": 5},
]

# 按优先级排序,同优先级按工作量升序
order = {"P0": 0, "P1": 1, "P2": 2}
sorted_cases = sorted(use_cases, key=lambda x: (order[x["priority"]], x["effort"]))
for uc in sorted_cases:
    print(f"[{uc['priority']}] {uc['role']}: {uc['action']} (effort={uc['effort']}d)")

输出:

TEXT 📖 仅展示
# 执行成功

(2) 非功能性需求

需求 目标 约束
QPS 百万级(集群) Nginx LB + K8s 弹性
P99 延迟 < 50ms Redis 缓存 + 异步 DB
可用性 99.9% 多副本 + 自动恢复
数据量 百万商品 + 千万价格 PostgreSQL 分区

4. 领域模型设计

(1) ER 图

100%
erDiagram
    User ||--o{ Subscription : has
    User ||--o{ Product : creates
    User ||--o{ Alert : sets
    Product ||--o{ Price : has
    Product ||--o{ Alert : watched_by
    Subscription ||--o{ Feature : includes
    
    User {
        int id PK
        string email UK
        string hashed_password
        string role
        datetime created_at
    }
    
    Product {
        int id PK
        string name
        string category
        float base_price
        string sku UK
        int user_id FK
        datetime created_at
    }
    
    Price {
        int id PK
        int product_id FK
        float price
        string currency
        string source
        datetime recorded_at
    }
    
    Subscription {
        int id PK
        int user_id FK
        string plan
        datetime starts_at
        datetime expires_at
    }
    
    Alert {
        int id PK
        int user_id FK
        int product_id FK
        float target_price
        string status
    }

(2) 聚合根识别

聚合根 包含实体 边界规则
User User, Subscription, Alert 用户 owns 订阅和告警
Product Product, Price 商品 owns 价格历史
PriceImport 批次信息, 导入状态 批量导入是独立事务

▶ 示例:领域事件定义

PYTHON
# domain/events.py
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum

class EventType(Enum):
    PRICE_CHANGED = "price_changed"
    ALERT_TRIGGERED = "alert_triggered"
    USER_SUBSCRIBED = "user_subscribed"

@dataclass
class DomainEvent:
    event_type: EventType
    aggregate_id: int
    occurred_at: datetime = field(default_factory=datetime.utcnow)
    payload: dict = field(default_factory=dict)

# 用法:价格变动事件
price_event = DomainEvent(
    event_type=EventType.PRICE_CHANGED,
    aggregate_id=42,
    payload={"old_price": 299.9, "new_price": 249.9, "currency": "CNY"},
)

输出:

TEXT 📖 仅展示
# 执行成功

5. 技术选型决策

(1) 选型对比

需求 选项 A 选项 B 决策 理由
Web 框架 FastAPI Flask/Django FastAPI 异步+自动文档+类型校验
数据库 PostgreSQL MySQL/MongoDB PostgreSQL JSONB+异步驱动成熟
缓存 Redis Memcached Redis 数据结构丰富+持久化
任务队列 Celery+Redis RQ/Dramatiq Celery 生态成熟+监控完善
实时通信 WebSocket SSE WebSocket 双向+低延迟
容器 Docker 裸机/VM Docker 一致性+编排

(2) 系统架构全景

100%
flowchart TD
    Client[Client / Bob Frontend] --> Nginx[Nginx Load Balancer]
    Nginx --> API1[FastAPI Pod 1]
    Nginx --> API2[FastAPI Pod 2]
    Nginx --> APIN[FastAPI Pod N]
    
    API1 --> PG_Master[(PostgreSQL Master)]
    API2 --> PG_Master
    APIN --> PG_Master
    
    PG_Master --> PG_Replica[(PostgreSQL Replica)]
    
    API1 --> Redis[(Redis Cluster)]
    API2 --> Redis
    APIN --> Redis
    
    Redis --> CW1[Celery Worker 1]
    Redis --> CW2[Celery Worker 2]
    
    CW1 --> PG_Master
    CW2 --> PG_Master
    
    API1 --> WS[WebSocket Hub]
    API2 --> WS
    
    Prometheus[Prometheus] --> API1
    Grafana[Grafana] --> Prometheus

6. API 版本化策略

(1) URL 版本 vs Header 版本

维度 URL 版本 /api/v1/ Header 版本 Accept: v=1
可见性 高(URL 明确) 低(隐藏在 Header)
路由 简单(前缀区分) 复杂(需中间件解析)
缓存 URL 独立缓存 需 Vary Header
Swagger 自动分组 需手动配置
推荐 ✅ PriceTracker 使用 适合内部 API

▶ 示例:URL 版本化路由结构

PYTHON
from fastapi import APIRouter

# V1 routes
v1_router = APIRouter(prefix="/api/v1", tags=["v1"])
v1_products = APIRouter(prefix="/products", tags=["products"])
v1_prices = APIRouter(prefix="/prices", tags=["prices"])
v1_auth = APIRouter(prefix="/auth", tags=["auth"])

# V2 routes (future)
v2_router = APIRouter(prefix="/api/v2", tags=["v2"])

# Mount routers
app.include_router(v1_auth)
app.include_router(v1_products, dependencies=[Depends(get_current_user)])
app.include_router(v1_prices, dependencies=[Depends(get_current_user)])
app.include_router(v1_router)

输出:

TEXT 📖 仅展示
# 执行成功

❓ 常见问题

Q DDD 的聚合根有什么用?
A 聚合根定义事务边界和一致性保证。Product 是聚合根意味着 Price 只能通过 Product 修改,不能独立增删,确保数据一致性。
Q PostgreSQL 读写分离怎么做?
A 主库写、从库读。SQLAlchemy 配置两个 engine(write_engine + read_engine),读操作用 read_engine 连从库。需处理复制延迟。
Q 为什么选 Redis 不选 Memcached?
A Redis 支持持久化、多种数据结构(Hash/Set/ZSet)、Pub/Sub,Memcached 只支持简单 KV。Redis 功能覆盖更广。
Q API 版本何时升级?
A 只在破坏性变更时升版本(如删除字段、改变响应结构)。新增字段和端点不算破坏性变更,不需要新版本。
Q 领域事件怎么实现?
A 简单方案用 Celery 任务(如 publish_price_updated_event.delay(product_id, new_price)),复杂方案用消息队列(RabbitMQ/Kafka)。
Q 架构图应该多详细?
A 对团队来说"够用"就行。过度设计浪费时间,设计不足导致混乱。PriceTracker 用三层架构+聚合根划分恰到好处。

📖 小节


📝 作业

  1. 基础题(难度⭐):画出 PriceTracker 的系统架构图(含 FastAPI、PostgreSQL、Redis、Celery、Nginx),标注每个组件的职责和数据流向。提示:Mermaid flowchart
  2. 进阶题(难度⭐⭐):设计 PriceTracker 的领域模型 ER 图,识别 3 个聚合根(User、Product、PriceImport),定义每个聚合包含的实体和边界规则。提示:Mermaid erDiagram + 聚合根表
  3. 挑战题(难度⭐⭐⭐):完成 PriceTracker 完整架构蓝图——需求文档(核心用例+非功能性需求)、技术选型对比表、API 版本化路由结构代码、部署拓扑图(含主从数据库、缓存分层、Celery 集群)。提示:综合本课所有内容

---|

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏