🏛️ SaaS 架构模式

单体 / 模块化单体 / 微服务 / Serverless / Edge — 何时选什么,为什么,怎么选。基于真实工程经验而非教条的决策框架。

01 架构选型:先问对问题

架构选型最忌「别人用什么我就用什么」。Netflix 用微服务是因为它有上千工程师、全球分布式部署、持续交付流水线——你的 3 人团队不需要。

真正驱动架构决策的 5 个变量:

变量问自己关键阈值
团队规模多少人在写代码?<20 人 → 单体;20-50 → 过渡区;50+ → 微服务可考虑
运维能力有 SRE 吗?有 K8s 经验吗?无自动化部署 → 别碰微服务
领域复杂度业务边界稳定吗?边界不清 → 先单体搞清楚
业务约束预算?合规?上市时间?预算紧 → 单体;合规隔离 → 微服务
增长轨迹验证期还是规模化期?MVP → 单体;10x 增长中 → 准备拆分
Conway 定律:你的系统架构会映射你的组织沟通结构。3 人团队建 8 个微服务 = 灾难。架构必须匹配组织,而非反过来。

02 五种架构模式详解

2.1 单体架构 (Monolith)

是什么:所有代码在一个代码库、一个进程、一次部署。前端、后端、数据库访问全在一起。

📁 典型单体项目结构
my-saas/
├── src/
│   ├── modules/
│   │   ├── auth/        # 认证模块
│   │   ├── billing/     # 计费模块
│   │   ├── users/       # 用户模块
│   │   └── api/         # API 入口
│   ├── db/              # 数据库访问
│   └── utils/           # 公共工具
├── package.json
└── docker-compose.yml   # 一个容器搞定

何时用

✅ 优势
  • 开发速度快,无需服务间通信
  • 调试简单,一个进程全链路追踪
  • 部署简单,一个 Docker 镜像
  • 事务一致性天然保证
  • 运维成本最低
❌ 劣势
  • 代码量增长后维护困难
  • 整体部署,牵一发动全身
  • 无法按模块独立扩展
  • 技术栈锁定,难以混用
  • 团队扩大后代码冲突频繁
🔧 实际代码:Express 单体 API
// server.ts — 单体架构,所有模块在同一进程
import express from 'express';
import { authRouter } from './modules/auth/routes';
import { billingRouter } from './modules/billing/routes';
import { userRouter } from './modules/users/routes';

const app = express();

// 共享数据库连接
import { prisma } from './db';
app.locals.prisma = prisma;

// 所有模块挂载在同一 Express 实例
app.use('/auth', authRouter);
app.use('/billing', billingRouter);
app.use('/users', userRouter);

// 一个端口,一个进程,一个部署
app.listen(3000, () => console.log('Monolith running :3000'));

2.2 模块化单体 (Modular Monolith)

是什么:代码组织像微服务(模块间有明确边界和接口),但部署仍是一个进程。是单体和微服务之间的最佳折中

这是 2025 年的推荐默认选择。Shopify、GitHub、Basecamp 都用此模式。你获得模块化的代码组织,又不需要微服务的运维负担。
📁 模块化单体结构 — 严格模块边界
my-saas/
├── src/
│   ├── modules/
│   │   ├── auth/
│   │   │   ├── auth.module.ts      # 模块入口,声明公开 API
│   │   │   ├── auth.service.ts     # 内部实现
│   │   │   ├── auth.controller.ts  # HTTP 入口
│   │   │   └── auth.types.ts       # 公开类型
│   │   ├── billing/
│   │   │   ├── billing.module.ts
│   │   │   ├── billing.service.ts
│   │   │   └── ...
│   │   └── users/
│   │       └── ...
│   ├── shared/                     # 极少共享代码
│   └── app.ts                      # 组装模块
├── package.json
└── tsconfig.json
🔧 模块间通信 — 通过公开接口,禁止直接访问
// modules/auth/auth.module.ts — 模块公开 API
export interface AuthModule {
  verifyToken(token: string): Promise<User>;
  createUser(input: CreateUserInput): Promise<User>;
}

// 内部实现不导出
class AuthService implements AuthModule {
  async verifyToken(token: string) { /* ... */ }
  async createUser(input: CreateUserInput) { /* ... */ }
}

// 只导出模块实例,不导出类
export const authModule: AuthModule = new AuthService();

// ❌ 错误:billing 直接 import auth 的内部文件
// import { hashPassword } from '../auth/auth.service';

// ✅ 正确:billing 通过模块公开接口调用
import { authModule } from '../auth/auth.module';
const user = await authModule.verifyToken(token);

强制模块边界:用 ESLint 或架构测试确保模块间不越界:

// architect.ts — 架构守卫测试
import { project } from 'ts-morph';

const p = project.addSourceFilesFromTsConfig('tsconfig.json');

// 确保 billing 模块不直接导入 auth 内部
for (const file of p.getSourceFiles('src/modules/billing/**/*.ts')) {
  const imports = file.getImportDeclarations();
  for (const imp of imports) {
    const path = imp.getModuleSpecifierValue();
    if (path.includes('modules/auth/') && !path.endsWith('auth.module')) {
      throw new Error(`架构违规: ${file.getFilePath()} 直接导入了 auth 内部: ${path}`);
    }
  }
}

2.3 微服务架构 (Microservices)

是什么:每个业务模块是独立的服务,独立部署、独立数据库、独立扩展。服务间通过 API/消息队列通信。

📁 微服务项目结构
my-saas/
├── services/
│   ├── auth-service/        # 独立代码库或 mono-repo 子目录
│   │   ├── src/
│   │   ├── Dockerfile
│   │   └── docker-compose.yml
│   ├── billing-service/
│   │   ├── src/
│   │   └── ...
│   ├── user-service/
│   │   └── ...
│   └── api-gateway/         # 统一入口
│       └── ...
├── infra/                   # 基础设施配置
│   ├── k8s/
│   └── terraform/
└── libs/                    # 共享库

何时用

⚠️ 微服务的隐形成本:一个 3 人团队管 8 个微服务 = 灾难。你需要:服务发现、分布式追踪、链路日志、熔断器、事件最终一致性、CI/CD 每个服务独立流水线、K8s 运维……这些成本在团队小时远超收益。
🔧 微服务间通信 — 同步 vs 异步
// === 同步:gRPC (适合强一致性场景) ===
// proto/billing.proto
syntax = "proto3";
service BillingService {
  rpc GetSubscription(GetSubscriptionReq) returns (Subscription);
}

// billing-service/src/server.ts
const server = new grpc.Server();
server.addService(billingProto.BillingService.service, {
  getSubscription: async (call, callback) => {
    const sub = await db.subscription.findUnique({ where: { userId: call.request.userId } });
    callback(null, sub);
  },
});

// === 异步:消息队列 (适合解耦和事件驱动) ===
// auth-service 发布事件
await messageQueue.publish('user.created', {
  userId: user.id,
  email: user.email,
  timestamp: Date.now(),
});

// billing-service 订阅事件
messageQueue.subscribe('user.created', async (event) => {
  await createFreeTrialSubscription(event.userId);
});
✅ 微服务优势
  • 独立部署,互不影响
  • 独立扩展,按需分配资源
  • 技术栈自由,每服务可选最佳
  • 故障隔离,一个挂不影响全局
  • 团队自治,并行开发
❌ 微服务劣势
  • 分布式系统复杂度爆炸
  • 网络延迟和不可靠性
  • 数据一致性需要额外工作
  • 运维成本 5-10x
  • 调试困难,跨服务追踪

2.4 Serverless 架构

是什么:你写函数(Function),云平台负责运行。无需管理服务器,按调用次数付费。AWS Lambda、Vercel Functions、Cloudflare Workers 是代表。

何时用

🔧 Serverless API — Vercel + Next.js
// app/api/subscribe/route.ts — Vercel Edge Function
import { stripe } from '@/lib/stripe';
import { kv } from '@vercel/kv';

export async function POST(req: Request) {
  const { plan, email } = await req.json();

  // 创建 Stripe Checkout Session
  const session = await stripe.checkout.sessions.create({
    customer_email: email,
    payment_method_types: ['card'],
    line_items: [{
      price: plan === 'pro' ? process.env.STRIPE_PRO_PRICE : process.env.STRIPE_FREE_PRICE,
      quantity: 1,
    }],
    mode: 'subscription',
    success_url: `${process.env.URL}/dashboard?upgraded=true`,
    cancel_url: `${process.env.URL}/pricing`,
  });

  // KV 存储状态(无需数据库!)
  await kv.set(`sub:${email}`, { status: 'pending', plan });

  return Response.json({ url: session.url });
}

// Cloudflare Workers 版本 — 更极致的 Edge
export default {
  async fetch(request: Request, env: Env) {
    const url = new URL(request.url);
    if (url.pathname === '/api/hello') {
      // 在全球 300+ 边缘节点执行,延迟 < 50ms
      return new Response(JSON.stringify({ hello: 'world', cf: request.cf?.country }));
    }
    return new Response('Not found', { status: 404 });
  },
};
✅ Serverless 优势
  • 零运维,不用管服务器
  • 自动扩展,从 0 到百万
  • 按使用付费,闲时成本几乎为零
  • 全球部署,Edge 低延迟
❌ Serverless 劣势
  • 冷启动延迟(50ms-5s)
  • 执行时间限制(Lambda 15min)
  • 状态管理复杂
  • 厂商锁定严重
  • 高频调用时成本可能超过 VPS

2.5 Edge 架构

是什么:代码运行在 CDN 边缘节点,离用户最近。Cloudflare Workers、Vercel Edge、Deno Deploy 是代表。本质是 Serverless 的进化——更轻、更快、更全球。

🔧 Edge + D1 (Cloudflare) — 全球分布式 SaaS
// wrangler.toml
name = "my-saas-api"
main = "src/index.ts"
compatibility_date = "2025-01-01"
[[d1_databases]]
binding = "DB"
database_name = "saas-db"
database_id = "xxx"

// src/index.ts
interface Env { DB: D1Database; }

export default {
  async fetch(request: Request, env: Env) {
    const url = new URL(request.url);

    // 在离用户最近的边缘节点执行
    if (url.pathname === '/api/users') {
      // D1 是 SQLite,也在边缘
      const { results } = await env.DB.prepare(
        'SELECT id, name, plan FROM users WHERE id = ?'
      ).bind(userId).run();
      return Response.json(results);
    }

    // 边缘 KV 缓存
    if (url.pathname === '/api/config') {
      const cached = await env.KV.get('app-config');
      if (cached) return new Response(cached, {
        headers: { 'content-type': 'application/json',
                   'cache-control': 'public, max-age=60' }
      });
    }

    return new Response('Not found', { status: 404 });
  },
};
Edge vs Serverless:Edge 是 Serverless 的子集但更极致——冷启动 <5ms(vs Lambda 500ms+),全球 300+ 节点自动部署,但执行时间限制更严(30s vs 15min),不支持长连接。适合 API 网关、认证检查、缓存、轻量计算。

03 五种模式对比矩阵

维度 单体 模块化单体 微服务 Serverless Edge
开发速度 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐ ⭐⭐⭐
运维复杂度 ⭐ (最低) ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐
扩展性 ⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
成本(低流量) ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
成本(高流量) ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐ ⭐⭐⭐
延迟 ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐⭐
团队要求 1-3 全栈 3-10 全栈 50+ 含 SRE 2-5 全栈 2-5 全栈
典型月成本 $5-50 VPS $5-50 VPS $500-5000+ $0-100 $0-50

04 决策树:我该选什么?

🤔 团队 < 20 人? → Yes → 📦 单体 or 模块化单体
🤔 团队 < 20 人? → No → 🤔 有 SRE/DevOps 团队? → No → 📦 模块化单体
🤔 有 SRE? → Yes → 🤔 不同模块扩展需求差异大? → Yes → 🔧 微服务
🤔 流量不均匀/事件驱动? → Yes → ☁️ Serverless → 需要 <50ms 全球延迟?→ 🌍 Edge
实战建议:90% 的 SaaS 项目应该从模块化单体开始。当你真正遇到扩展瓶颈时,模块化单体的明确边界让拆分微服务变得顺理成章——而不是从一个大泥球中强行切割。

05 从单体到微服务的渐进拆分路径

不要一次性重写。以下是经过验证的渐进拆分策略:

1️⃣ 模块化单体 2️⃣ 抽取独立服务 3️⃣ 数据库拆分 4️⃣ 独立部署

第 1 步:模块化单体(建立边界)

在单体中建立严格的模块边界。用架构测试强制执行。这一步不改变部署方式,只改变代码组织。

第 2 步:抽取最独立的模块(先摘低垂的果实)

🔧 优先拆分:通知服务(最易独立)
// 1. 先在单体中定义异步接口
interface NotificationService {
  sendEmail(to: string, template: string, data: object): Promise<void>;
  sendSMS(phone: string, message: string): Promise<void>;
}

// 2. 切换到消息队列实现(仍然在单体中)
class QueueNotificationService implements NotificationService {
  async sendEmail(to, template, data) {
    await queue.publish('notification.email', { to, template, data });
  }
}

// 3. 拆出独立服务消费队列
// notification-service/main.ts
queue.subscribe('notification.email', async (msg) => {
  await sendgrid.send({ to: msg.to, templateId: msg.template, data: msg.data });
});

第 3 步:数据库拆分(最关键也最危险)

🔧 数据库拆分策略 — 先共享读,再独立写
// Phase 1: 服务独立部署,但共享数据库(读副本)
// notification-service 仍然读取主库的 users 表

// Phase 2: 引入事件同步,逐步迁移到独立数据库
// user-service 发布事件
await eventBus.publish('user.updated', {
  userId: user.id,
  email: user.email,
  name: user.name,
});

// notification-service 维护本地副本
eventBus.subscribe('user.updated', async (event) => {
  await localDb.users.upsert({
    where: { id: event.userId },
    update: { email: event.email, name: event.name },
    create: { id: event.userId, email: event.email, name: event.name },
  });
});

// Phase 3: 完全独立,不再读取主库

第 4 步:独立部署流水线

# .github/workflows/notification-service.yml
name: Deploy Notification Service
on:
  push:
    paths: ['services/notification-service/**']
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - run: docker build -t notification-service ./services/notification-service
      - run: docker push ${{ env.REGISTRY }}/notification-service:${{ github.sha }}
      - run: kubectl set image deployment/notification-service app=${{ env.REGISTRY }}/notification-service:${{ github.sha }}

06 SaaS 必备架构模式

无论选择哪种架构风格,以下模式是 SaaS 的「标配」:

6.1 API Gateway 模式

统一入口:路由、认证、限流、日志。

🔧 Kong / 自建 API Gateway
// gateway/index.ts — 基于Express的轻量API网关
import rateLimit from 'express-rate-limit';
import { createProxyMiddleware } from 'http-proxy-middleware';

const app = express();

// 全局限流
app.use(rateLimit({ windowMs: 60_000, max: 100 }));

// JWT 认证中间件
app.use('/api/*', async (req, res, next) => {
  const token = req.headers.authorization?.replace('Bearer ', '');
  if (!token) return res.status(401).json({ error: 'Missing token' });
  try {
    req.user = await verifyJWT(token);
    next();
  } catch {
    res.status(401).json({ error: 'Invalid token' });
  }
});

// 路由到后端服务
app.use('/api/auth', createProxyMiddleware({ target: 'http://auth-service:3001' }));
app.use('/api/billing', createProxyMiddleware({ target: 'http://billing-service:3002' }));
app.use('/api/users', createProxyMiddleware({ target: 'http://user-service:3003' }));

app.listen(8000);

6.2 CQRS(命令查询职责分离)

写入和读取用不同的模型。SaaS 中极其常见——管理后台需要复杂查询,API 需要简单读取。

🔧 CQRS with Event Sourcing
// Write side — 命令处理,只关心业务规则
class SubscriptionCommandHandler {
  async handleUpgrade(cmd: UpgradeSubscription) {
    const sub = await this.repo.findById(cmd.subscriptionId);
    sub.upgrade(cmd.newPlan);  // 业务逻辑
    await this.repo.save(sub);
    await this.eventBus.publish(new SubscriptionUpgraded(sub.id, cmd.newPlan));
  }
}

// Read side — 查询模型,为读优化
class SubscriptionQueryService {
  async getSubscriptionView(userId: string) {
    // 从读取专用表查询,可能包含跨服务聚合的数据
    return this.readDb.query(`
      SELECT s.*, p.features, u.name as user_name
      FROM subscription_views s
      JOIN plan_features p ON s.plan_id = p.id
      JOIN user_views u ON s.user_id = u.id
      WHERE s.user_id = ?
    `, [userId]);
  }
}

// 事件同步写入模型 → 读取模型
eventBus.subscribe('SubscriptionUpgraded', async (event) => {
  await readDb.subscriptionViews.update({
    where: { id: event.subscriptionId },
    data: { plan: event.newPlan, updatedAt: new Date() },
  });
});

6.3 Circuit Breaker(熔断器)

微服务中防止级联故障的关键模式。

🔧 熔断器实现
import CircuitBreaker from 'opossum';

// 包装外部服务调用
const callBillingService = new CircuitBreaker(
  async (userId: string) => {
    const res = await fetch(`http://billing-service:3002/api/subscriptions/${userId}`);
    if (!res.ok) throw new Error(`Billing service: ${res.status}`);
    return res.json();
  },
  {
    timeout: 3000,        // 3s 超时
    errorThreshold: 50,   // 50% 错误率触发熔断
    resetTimeout: 30000,  // 30s 后尝试恢复
  }
);

// 熔断时返回降级数据
callBillingService.fallback(() => ({
  plan: 'free',
  status: 'degraded',  // 标记为降级数据
  features: FREE_TIER_FEATURES,
}));

// 使用
const subscription = await callBillingService.fire(userId);

6.4 Saga 模式(分布式事务)

微服务下替代分布式锁的方案。每个步骤有对应的补偿操作。

🔧 编排式 Saga — 用户注册流程
// 用户注册需要:创建用户 → 初始化订阅 → 发送欢迎邮件
// 任何一步失败,执行之前步骤的补偿操作

async function registerUserSaga(input: RegisterInput) {
  const saga = new Saga();

  // Step 1: 创建用户
  saga.addStep({
    execute: async () => {
      const user = await userService.create(input);
      return { userId: user.id };
    },
    compensate: async (ctx) => {
      await userService.delete(ctx.userId);
    },
  });

  // Step 2: 初始化订阅
  saga.addStep({
    execute: async (ctx) => {
      const sub = await billingService.initFreeSubscription(ctx.userId);
      return { subscriptionId: sub.id };
    },
    compensate: async (ctx) => {
      await billingService.cancelSubscription(ctx.subscriptionId);
    },
  });

  // Step 3: 发送欢迎邮件(失败不需要补偿)
  saga.addStep({
    execute: async (ctx) => {
      await emailService.sendWelcome(ctx.userId);
    },
    compensate: async () => { /* 邮件已发就算了 */ },
  });

  await saga.execute();
  // 如果 Step 2 失败,自动执行 Step 1 的 compensate(删除用户)
}

6.5 Strangler Fig 模式(绞杀者无花果)

渐进式从旧系统迁移到新系统。新功能走新架构,旧功能逐步替换。

🔧 路由层实现绞杀模式
// gateway/strangler.ts — 逐步将路由从旧系统切到新系统
const LEGACY_BACKEND = 'http://legacy-app:3000';
const NEW_BACKEND = 'http://new-service:4000';

// 迁移路由表:哪些路径已切到新系统
const migratedPaths = new Set([
  '/api/auth',         // Week 1: 认证模块已迁移
  '/api/users/profile', // Week 2: 用户资料已迁移
  // '/api/billing',    // Week 3: 计划迁移中...
]);

app.use('/api/*', (req, res, next) => {
  const migrated = Array.from(migratedPaths).some(p => req.path.startsWith(p));
  const target = migrated ? NEW_BACKEND : LEGACY_BACKEND;
  createProxyMiddleware({ target })(req, res, next);
});

07 SaaS 特有架构考量

7.1 多租户数据隔离

详细内容见 多租户架构 专页,这里概述三种策略:

策略隔离级别成本适合场景
共享数据库 + Row-Level逻辑隔离最低中小型 SaaS,每个 query 加 WHERE tenant_id = ?
Schema 隔离中等中等需要一定隔离,但不想管多个数据库
独立数据库物理隔离最高企业级,合规要求严格

7.2 可配置性架构

每个租户可能有不同的功能开关、UI 配置、工作流。你需要一个特性旗标 + 配置系统

🔧 基于租户的特性旗标
// lib/features.ts
interface FeatureConfig {
  maxProjects: number;
  hasAI: boolean;
  hasSSO: boolean;
  customBranding: boolean;
}

const PLAN_FEATURES: Record<string, FeatureConfig> = {
  free:    { maxProjects: 3,   hasAI: false, hasSSO: false, customBranding: false },
  pro:     { maxProjects: 50,  hasAI: true,  hasSSO: false, customBranding: false },
  enterprise: { maxProjects: Infinity, hasAI: true, hasSSO: true, customBranding: true },
};

// 中间件:注入当前租户特性
function featureFlag(req: Request, res: Response, next: NextFunction) {
  const tenant = req.user.tenant;
  const plan = tenant.plan; // 从数据库或缓存获取
  req.features = PLAN_FEATURES[plan];
  next();
}

// 使用
app.post('/api/projects', featureFlag, (req, res) => {
  if (req.user.projects.length >= req.features.maxProjects) {
    return res.status(403).json({ error: 'Project limit reached', upgradeUrl: '/pricing' });
  }
  // 创建项目...
});

7.3 审计日志

SaaS 必须记录谁在什么时候做了什么。不是可选的。

🔧 审计日志中间件
// middleware/audit.ts
interface AuditLog {
  tenantId: string;
  userId: string;
  action: string;      // 'project.create', 'user.invite', 'settings.update'
  resource: string;    // '/api/projects/123'
  before?: object;     // 变更前
  after?: object;      // 变更后
  ip: string;
  userAgent: string;
  timestamp: Date;
}

async function auditLog(req: Request, action: string, before?: object, after?: object) {
  await db.auditLog.create({
    data: {
      tenantId: req.user.tenantId,
      userId: req.user.id,
      action,
      resource: req.originalUrl,
      before: before ?? null,
      after: after ?? null,
      ip: req.ip,
      userAgent: req.headers['user-agent'],
    },
  });
}

// 使用
app.put('/api/settings', async (req, res) => {
  const before = await getSettings(req.user.tenantId);
  const after = await updateSettings(req.user.tenantId, req.body);
  await auditLog(req, 'settings.update', before, after);
  res.json(after);
});

08 混合架构:现实中的最优解

真实项目很少是「纯」某种架构。最常见的模式:

🏗️ 推荐混合架构:模块化单体核心 + Serverless 边缘
┌─────────────────────────────────────────────────┐
│              Cloudflare Workers (Edge)            │
│  ┌───────────┐ ┌───────────┐ ┌────────────────┐ │
│  │ Auth Gate │ │ Rate Limit│ │ Static Cache   │ │
│  └─────┬─────┘ └─────┬─────┘ └───────┬────────┘ │
└────────┼─────────────┼───────────────┼──────────┘
         │             │               │
         ▼             ▼               ▼
┌─────────────────────────────────────────────────┐
│           模块化单体 (VPS / Container)            │
│  ┌────────┐ ┌─────────┐ ┌──────┐ ┌───────────┐ │
│  │  Auth  │ │ Billing │ │ Users│ │ Projects  │ │
│  └────────┘ └─────────┘ └──────┘ └───────────┘ │
│  └───────────── PostgreSQL ─────────────────────┘│
└────────────────────────┬────────────────────────┘
                         │ 消息队列
                         ▼
┌─────────────────────────────────────────────────┐
│          Serverless Workers (异步任务)            │
│  ┌────────────┐ ┌──────────┐ ┌───────────────┐ │
│  │ Email Send │ │ PDF Gen  │ │ Image Process │ │
│  └────────────┘ └──────────┘ └───────────────┘ │
└─────────────────────────────────────────────────┘

这种组合让你:

🔧 混合架构路由示例
// Cloudflare Worker — Edge 路由层
export default {
  async fetch(request: Request, env: Env) {
    const url = new URL(request.url);

    // 静态资源 → CDN 缓存,零延迟
    if (url.pathname.startsWith('/static/')) {
      return caches.default.match(request) || fetch(request);
    }

    // 认证检查 → Edge JWT 验证,<5ms
    const user = await verifyJWT(request.headers.get('Authorization'), env.JWT_SECRET);
    if (!user && !PUBLIC_PATHS.includes(url.pathname)) {
      return new Response('Unauthorized', { status: 401 });
    }

    // API 请求 → 转发到单体核心
    if (url.pathname.startsWith('/api/')) {
      return fetch(new Request(request, {
        headers: { ...Object.fromEntries(request.headers), 'X-User': JSON.stringify(user) },
      }));
    }

    return fetch(request);
  },
};

09 常见反模式与教训

❌ 反模式 1:过早微服务化

3 人团队建 12 个微服务。结果:80% 时间花在运维上,功能迭代速度暴跌。先单体验证,再按需拆分。

❌ 反模式 2:共享数据库的「假」微服务

服务独立部署了,但所有服务直连同一个数据库。一个服务改表结构,其他全挂。每个服务应该有自己的数据存储。

❌ 反模式 3:同步链式调用

A → B → C → D,四跳同步调用。延迟叠加,任何一环出问题全链失败。用消息队列解耦,或合并为一个服务。

❌ 反模式 4:忽略 observability

微服务没有分布式追踪 = 黑箱。从第一天起就要有:请求 ID 透传、结构化日志、指标采集。推荐 OpenTelemetry

❌ 反模式 5:Big Bang 重写

「我们暂停新功能,花 6 个月重写架构」。结果:6 个月后产品已落后,新架构也未必更好。用 Strangler Fig 模式渐进迁移。

10 技术选型速查

需求推荐替代
API GatewayKong / 自建 ExpressTraefik, AWS API Gateway
消息队列Redis Streams / BullMQRabbitMQ, Kafka, Inngest
服务发现K8s Service (如用 K8s)Consul, DNS
分布式追踪OpenTelemetry + JaegerZipkin, Datadog APM
熔断器opossumPolly (.NET), resilience4j
特性旗标PostHog / 自建LaunchDarkly, Unleash
Serverless 平台VercelAWS Lambda, Netlify
Edge 平台Cloudflare WorkersDeno Deploy, Vercel Edge
容器编排Docker Compose (小规模)K8s (大规模), Nomad

11 与 Agent 架构的关联

SaaS 架构决策直接影响 Agent 系统的设计:

SaaS 架构选择对 Agent 的影响参考
单体 → 微服务拆分 Agent 工具系统对应微服务边界;每个服务暴露为 Agent Tool 🛠️ 工具系统
CQRS 读写分离 Agent 读取用查询模型,写入走命令模型;减少 Agent 上下文负担 🧠 记忆系统
消息队列解耦 Agent 异步工具调用通过队列;Background Review Fork 模式 🔄 主循环
多租户数据隔离 Agent 的知识库/记忆必须按租户隔离 🔒 安全机制
Edge 认证网关 Agent API 调用经 Edge 认证,Policy Pipeline 在网关层执行 🔒 安全机制
特性旗标 Agent 功能按租户计划开放;Pro 用户才能用 AI Agent 🚀 生产级特性
从 Agent 研究中我们发现:OpenClaw 的架构正是混合模式的典范——Edge 层 (Telegram/Discord adapter) + 核心单体 (Gateway) + 异步 Worker (Cron/Subagent)。而 Hermes 的 76 个工具系统展示了微服务边界如何自然映射为 Agent Tool 边界。详见 工具系统多 Agent 协作

12 推荐阅读