Skip to content

引言:为什么你的 SaaS 死在了第 2 个客户?

在 SaaS 创业的早期阶段,团队往往专注于快速实现业务功能,争取早日上线。然而,许多团队在获得第一个客户后,却在第二个客户面前碰壁——不是功能做不出来,而是系统无法扩展。

一个真实的创业故事

让我们从一个真实的案例开始。某创业团队开发了一套电商后台管理系统,为第一个客户定制完成后上线运行良好。当第二个客户要求接入系统时,团队发现了一个致命问题:

typescript
// 错误的做法:硬编码客户ID
@Entity()
export class Order {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  customerId: number = 1; // 硬编码第一个客户的ID

  @Column()
  amount: number;

  @Column()
  status: string;
}

当需要支持第二个客户时,团队意识到几乎每个模块都需要重构,因为客户ID被硬编码在系统各处。最终,项目延期3个月,客户流失,团队士气大挫。

横切关注点:系统可扩展性的隐形骨架

这个案例暴露了一个核心问题:横切关注点(Cross-Cutting Concerns)没有得到妥善处理。

横切关注点是指那些跨越多个模块、影响整个系统的关注点,如:

  • 多租户隔离
  • 认证授权
  • 日志审计
  • 异常处理
  • 缓存策略
  • 事务管理

这些关注点不像业务功能那样直观,却是系统可扩展性的隐形骨架。忽视它们,系统就会像沙滩上的城堡,看似美丽却经不起风浪。

最小正确抽象原则

面对横切关注点,我们需要遵循"最小正确抽象"原则:

  1. 不要过度设计:在 MVP 阶段,不需要实现所有可能的功能
  2. 保留扩展性:确保当前设计不会阻碍未来的扩展
  3. 关注核心问题:解决当前最紧迫的问题,而不是想象中的问题
typescript
// 正确的做法:预留扩展点
@Entity()
export class Order {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  tenantId: string; // 租户ID,为多租户扩展预留

  @Column()
  amount: number;

  @Column()
  status: string;

  @Column({ nullable: true })
  metadata: string; // 元数据字段,为未来扩展预留
}

专栏目标

本专栏将带领你逐一剖析 SaaS 系统中的12个关键横切关注点,每篇文章将包含:

  1. 问题分析:识别常见陷阱和反模式
  2. NestJS 实践:提供可落地的代码示例
  3. 创业建议:根据团队发展阶段给出优先级建议
  4. 真实案例:通过事故案例加深理解

我们的目标是让你在第1周就能做出不会在未来杀死公司的设计决策。

专栏结构

本专栏共包含12篇文章,分为以下几个部分:

  • 基础设施层:多租户、认证、授权
  • 质量保障层:日志审计、异常处理、输入校验
  • 性能优化层:事务管理、缓存策略
  • 运维支撑层:可观测性、配置管理
  • 演进保障层:API版本管理、测试支持

每篇文章都会结合 NestJS 框架的具体实现,提供可复制的代码模板和最佳实践。

写作约定

在本专栏中,我们将使用以下技术栈:

  • 框架:NestJS 9+
  • 语言:TypeScript
  • 数据库:TypeORM + PostgreSQL
  • 其他:JWT、Redis、Prometheus 等

所有的代码示例都将遵循 NestJS 的最佳实践,包括模块化设计、依赖注入、装饰器模式等。


准备好深入探索 SaaS 系统的横切关注点世界了吗?让我们从第一个也是最重要的关注点开始:多租户上下文。