引言:为什么你的 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)没有得到妥善处理。
横切关注点是指那些跨越多个模块、影响整个系统的关注点,如:
- 多租户隔离
- 认证授权
- 日志审计
- 异常处理
- 缓存策略
- 事务管理
这些关注点不像业务功能那样直观,却是系统可扩展性的隐形骨架。忽视它们,系统就会像沙滩上的城堡,看似美丽却经不起风浪。
最小正确抽象原则
面对横切关注点,我们需要遵循"最小正确抽象"原则:
- 不要过度设计:在 MVP 阶段,不需要实现所有可能的功能
- 保留扩展性:确保当前设计不会阻碍未来的扩展
- 关注核心问题:解决当前最紧迫的问题,而不是想象中的问题
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个关键横切关注点,每篇文章将包含:
- 问题分析:识别常见陷阱和反模式
- NestJS 实践:提供可落地的代码示例
- 创业建议:根据团队发展阶段给出优先级建议
- 真实案例:通过事故案例加深理解
我们的目标是让你在第1周就能做出不会在未来杀死公司的设计决策。
专栏结构
本专栏共包含12篇文章,分为以下几个部分:
- 基础设施层:多租户、认证、授权
- 质量保障层:日志审计、异常处理、输入校验
- 性能优化层:事务管理、缓存策略
- 运维支撑层:可观测性、配置管理
- 演进保障层:API版本管理、测试支持
每篇文章都会结合 NestJS 框架的具体实现,提供可复制的代码模板和最佳实践。
写作约定
在本专栏中,我们将使用以下技术栈:
- 框架:NestJS 9+
- 语言:TypeScript
- 数据库:TypeORM + PostgreSQL
- 其他:JWT、Redis、Prometheus 等
所有的代码示例都将遵循 NestJS 的最佳实践,包括模块化设计、依赖注入、装饰器模式等。
准备好深入探索 SaaS 系统的横切关注点世界了吗?让我们从第一个也是最重要的关注点开始:多租户上下文。