主题
第 4 章:架构和最佳实践
欢迎来到架构和最佳实践的学习!本章将深入探讨如何设计高质量、可维护的软件架构,以及遵循哪些最佳实践来编写专业级代码。
📚 本章目标
完成本章后,你将能够:
- ✅ 理解和应用 SOLID 原则
- ✅ 掌握分层架构设计
- ✅ 实现高效的错误处理策略
- ✅ 建立完善的日志和监控系统
- ✅ 掌握配置管理最佳实践
- ✅ 编写清晰、可维护的代码
- ✅ 进行有效的代码审查
📖 本章内容
1. SOLID 原则深入
SOLID 原则是面向对象设计的五大基本原则,它们帮助我们创建更易于理解、灵活和可维护的软件。
1.1 单一职责原则(SRP)
🔪 The Metaphor: The Swiss Army Knife (Anti-pattern) 违反 SRP 的类就像是一把瑞士军刀。 它能剪指甲、开红酒、锯木头、切菜。 看起来很全能,但当你只想切菜时,你得举着整把沉重的刀;当你只想修指甲时,你可能会不小心划伤自己。 SRP 告诉我们要把瑞士军刀拆开,变成一把手术刀、一把开瓶器、一把锯子。 这样,当你想升级“锯木头”的功能时,你不需要把“剪指甲”的部分也回炉重造。
原则: 一个类应该只有一个引起它变化的原因。
实战示例:用户管理
typescript
// ❌ 违反 SRP:一个类承担多个职责
class User {
constructor(
public id: string,
public name: string,
public email: string
) {}
// 职责1:数据验证
validate(): boolean {
if (!this.email.includes('@')) {
return false;
}
if (this.name.length < 2) {
return false;
}
return true;
}
// 职责2:数据持久化
save(): void {
// 保存到数据库
console.log('保存用户到数据库');
}
// 职责3:发送邮件
sendWelcomeEmail(): void {
console.log(`发送欢迎邮件到 ${this.email}`);
}
// 职责4:生成报告
generateReport(): string {
return `用户报告: ${this.name}`;
}
}
// ✅ 遵循 SRP:每个类只有一个职责
class User {
constructor(
public readonly id: string,
public readonly name: string,
public readonly email: string
) {}
}
class UserValidator {
validate(user: User): { valid: boolean; errors: string[] } {
const errors: string[] = [];
if (!user.email.includes('@')) {
errors.push('邮箱格式不正确');
}
if (user.name.length < 2) {
errors.push('用户名太短');
}
return {
valid: errors.length === 0,
errors
};
}
}
class UserRepository {
async save(user: User): Promise<void> {
console.log('保存用户到数据库:', user);
// 实际的数据库操作
}
async findById(id: string): Promise<User | null> {
console.log('从数据库查找用户:', id);
// 实际的数据库查询
return null;
}
}
class UserEmailService {
async sendWelcomeEmail(user: User): Promise<void> {
console.log(`发送欢迎邮件到 ${user.email}`);
// 实际的邮件发送逻辑
}
}
class UserReportGenerator {
generate(user: User): string {
return `
=== 用户报告 ===
ID: ${user.id}
姓名: ${user.name}
邮箱: ${user.email}
================
`.trim();
}
}
// 使用示例
const user = new User('1', 'Alice', 'alice@example.com');
const validator = new UserValidator();
const validationResult = validator.validate(user);
if (validationResult.valid) {
const repository = new UserRepository();
await repository.save(user);
const emailService = new UserEmailService();
await emailService.sendWelcomeEmail(user);
const reportGenerator = new UserReportGenerator();
const report = reportGenerator.generate(user);
console.log(report);
} else {
console.log('验证失败:', validationResult.errors);
}1.2 开闭原则(OCP)
🔌 The Metaphor: The USB Port 你的电脑(核心系统)是封闭的。你不能随意拆开机箱去焊接电路板(修改现有代码)。 但电脑通过 USB 接口是开放的。你可以插入鼠标、键盘、打印机、U盘(扩展功能)。 只要外设符合 USB 标准(接口契约),电脑就能使用它,而不需要知道它具体是什么牌子。 OCP 的目标就是把软件设计成这样:通过“插件”来扩展功能,而不是通过“手术”。
原则: 软件实体应该对扩展开放,对修改封闭。
实战示例:支付处理系统
typescript
// ❌ 违反 OCP:添加新支付方式需要修改现有代码
class PaymentProcessor {
processPayment(amount: number, type: string): void {
if (type === 'creditCard') {
console.log(`使用信用卡支付 ¥${amount}`);
// 信用卡支付逻辑
} else if (type === 'alipay') {
console.log(`使用支付宝支付 ¥${amount}`);
// 支付宝支付逻辑
} else if (type === 'wechat') {
console.log(`使用微信支付 ¥${amount}`);
// 微信支付逻辑
}
// 每次添加新支付方式都要修改这个方法!
}
}
// ✅ 遵循 OCP:添加新支付方式无需修改现有代码
interface PaymentMethod {
pay(amount: number): Promise<PaymentResult>;
}
interface PaymentResult {
success: boolean;
transactionId: string;
message: string;
}
class CreditCardPayment implements PaymentMethod {
constructor(
private cardNumber: string,
private cvv: string
) {}
async pay(amount: number): Promise<PaymentResult> {
console.log(`💳 信用卡支付 ¥${amount}`);
// 信用卡支付逻辑
return {
success: true,
transactionId: 'CC-' + Date.now(),
message: '支付成功'
};
}
}
class AlipayPayment implements PaymentMethod {
constructor(private account: string) {}
async pay(amount: number): Promise<PaymentResult> {
console.log(`💰 支付宝支付 ¥${amount}`);
// 支付宝支付逻辑
return {
success: true,
transactionId: 'ALIPAY-' + Date.now(),
message: '支付成功'
};
}
}
class WeChatPayment implements PaymentMethod {
constructor(private openId: string) {}
async pay(amount: number): Promise<PaymentResult> {
console.log(`💚 微信支付 ¥${amount}`);
// 微信支付逻辑
return {
success: true,
transactionId: 'WECHAT-' + Date.now(),
message: '支付成功'
};
}
}
// 新增支付方式无需修改现有代码
class CryptoPayment implements PaymentMethod {
constructor(
private walletAddress: string,
private cryptoType: string
) {}
async pay(amount: number): Promise<PaymentResult> {
console.log(`₿ ${this.cryptoType}支付 ¥${amount}`);
// 加密货币支付逻辑
return {
success: true,
transactionId: 'CRYPTO-' + Date.now(),
message: '支付成功'
};
}
}
// 支付处理器(遵循 OCP)
class PaymentProcessor {
async process(paymentMethod: PaymentMethod, amount: number): Promise<PaymentResult> {
console.log(`\n处理支付: ¥${amount}`);
const result = await paymentMethod.pay(amount);
console.log(`结果: ${result.message}`);
return result;
}
}
// 使用示例
const processor = new PaymentProcessor();
await processor.process(
new CreditCardPayment('1234-5678-9012-3456', '123'),
100
);
await processor.process(
new AlipayPayment('user@example.com'),
200
);
await processor.process(
new CryptoPayment('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', 'Ethereum'),
300
);2. 分层架构
分层架构将应用程序分为多个层次,每层有明确的职责。
2.1 三层架构
🍝 The Metaphor: The Lasagna 分层架构就像是一份千层面 (Lasagna)。
- 每一层(面皮、肉酱、奶酪)都有自己独特的味道和作用。
- 你不能把肉酱直接涂在盘子底(跳过层级),也不能把面皮铺在最上面(层级倒置)。
- 好处:你可以把中间的“肉酱层”换成“素菜层”,而不需要动上下的面皮。
- 坏处:如果你只是想吃一口面,你得切穿所有层(简单的 CRUD 操作也得穿过 Controller -> Service -> Repository)。
层次划分:
- 表示层(Presentation Layer) - 处理 UI 和用户交互
- 业务逻辑层(Business Logic Layer) - 处理业务规则
- 数据访问层(Data Access Layer) - 处理数据持久化
完整示例:任务管理系统
typescript
// ============= 数据访问层(DAL) =============
// 实体定义
interface Task {
id: string;
title: string;
description: string;
status: 'pending' | 'in-progress' | 'completed';
priority: 'low' | 'medium' | 'high';
createdAt: Date;
updatedAt: Date;
}
// 数据库接口
interface IDatabase {
query<T>(sql: string, params?: any[]): Promise<T[]>;
execute(sql: string, params?: any[]): Promise<void>;
}
// 模拟数据库实现
class MockDatabase implements IDatabase {
private data = new Map<string, any[]>();
async query<T>(sql: string, params?: any[]): Promise<T[]> {
console.log(`[DB] 查询: ${sql}`);
// 模拟数据库查询
return [];
}
async execute(sql: string, params?: any[]): Promise<void> {
console.log(`[DB] 执行: ${sql}`);
// 模拟数据库执行
}
}
// 仓库接口
interface ITaskRepository {
findAll(): Promise<Task[]>;
findById(id: string): Promise<Task | null>;
create(task: Omit<Task, 'id' | 'createdAt' | 'updatedAt'>): Promise<Task>;
update(id: string, task: Partial<Task>): Promise<Task>;
delete(id: string): Promise<boolean>;
}
// 仓库实现
class TaskRepository implements ITaskRepository {
constructor(private db: IDatabase) {}
async findAll(): Promise<Task[]> {
console.log('[Repository] 查找所有任务');
const rows = await this.db.query<Task>('SELECT * FROM tasks');
return rows;
}
async findById(id: string): Promise<Task | null> {
console.log(`[Repository] 查找任务: ${id}`);
const rows = await this.db.query<Task>(
'SELECT * FROM tasks WHERE id = ?',
[id]
);
return rows[0] || null;
}
async create(taskData: Omit<Task, 'id' | 'createdAt' | 'updatedAt'>): Promise<Task> {
console.log('[Repository] 创建任务');
const task: Task = {
id: Math.random().toString(36).substr(2, 9),
...taskData,
createdAt: new Date(),
updatedAt: new Date()
};
await this.db.execute(
'INSERT INTO tasks (id, title, description, status, priority, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?, ?)',
[task.id, task.title, task.description, task.status, task.priority, task.createdAt, task.updatedAt]
);
return task;
}
async update(id: string, taskData: Partial<Task>): Promise<Task> {
console.log(`[Repository] 更新任务: ${id}`);
const task = await this.findById(id);
if (!task) {
throw new Error('任务不存在');
}
const updated = {
...task,
...taskData,
updatedAt: new Date()
};
await this.db.execute(
'UPDATE tasks SET title = ?, description = ?, status = ?, priority = ?, updatedAt = ? WHERE id = ?',
[updated.title, updated.description, updated.status, updated.priority, updated.updatedAt, id]
);
return updated;
}
async delete(id: string): Promise<boolean> {
console.log(`[Repository] 删除任务: ${id}`);
await this.db.execute('DELETE FROM tasks WHERE id = ?', [id]);
return true;
}
}
// ============= 业务逻辑层(BLL) =============
// 业务规则
class TaskBusinessRules {
static canMarkAsCompleted(task: Task): boolean {
return task.status !== 'completed';
}
static canDelete(task: Task): boolean {
// 业务规则:只能删除已完成或待处理的任务
return task.status === 'completed' || task.status === 'pending';
}
static validateTask(task: Partial<Task>): { valid: boolean; errors: string[] } {
const errors: string[] = [];
if (task.title && task.title.length < 3) {
errors.push('任务标题至少需要 3 个字符');
}
if (task.title && task.title.length > 100) {
errors.push('任务标题不能超过 100 个字符');
}
if (task.description && task.description.length > 1000) {
errors.push('任务描述不能超过 1000 个字符');
}
return {
valid: errors.length === 0,
errors
};
}
}
// 业务服务接口
interface ITaskService {
getAllTasks(): Promise<Task[]>;
getTaskById(id: string): Promise<Task>;
createTask(taskData: Omit<Task, 'id' | 'createdAt' | 'updatedAt'>): Promise<Task>;
updateTask(id: string, taskData: Partial<Task>): Promise<Task>;
markAsCompleted(id: string): Promise<Task>;
deleteTask(id: string): Promise<void>;
}
// 业务服务实现
class TaskService implements ITaskService {
constructor(
private repository: ITaskRepository,
private logger?: ILogger
) {}
async getAllTasks(): Promise<Task[]> {
this.logger?.log('[Service] 获取所有任务');
return await this.repository.findAll();
}
async getTaskById(id: string): Promise<Task> {
this.logger?.log(`[Service] 获取任务: ${id}`);
const task = await this.repository.findById(id);
if (!task) {
throw new Error('任务不存在');
}
return task;
}
async createTask(taskData: Omit<Task, 'id' | 'createdAt' | 'updatedAt'>): Promise<Task> {
this.logger?.log('[Service] 创建任务');
// 验证
const validation = TaskBusinessRules.validateTask(taskData);
if (!validation.valid) {
throw new Error(`验证失败: ${validation.errors.join(', ')}`);
}
// 创建
const task = await this.repository.create(taskData);
this.logger?.log(`[Service] 任务已创建: ${task.id}`);
return task;
}
async updateTask(id: string, taskData: Partial<Task>): Promise<Task> {
this.logger?.log(`[Service] 更新任务: ${id}`);
// 验证
const validation = TaskBusinessRules.validateTask(taskData);
if (!validation.valid) {
throw new Error(`验证失败: ${validation.errors.join(', ')}`);
}
// 更新
const task = await this.repository.update(id, taskData);
this.logger?.log(`[Service] 任务已更新: ${id}`);
return task;
}
async markAsCompleted(id: string): Promise<Task> {
this.logger?.log(`[Service] 标记任务为已完成: ${id}`);
const task = await this.getTaskById(id);
// 检查业务规则
if (!TaskBusinessRules.canMarkAsCompleted(task)) {
throw new Error('任务已经完成');
}
// 更新状态
return await this.repository.update(id, { status: 'completed' });
}
async deleteTask(id: string): Promise<void> {
this.logger?.log(`[Service] 删除任务: ${id}`);
const task = await this.getTaskById(id);
// 检查业务规则
if (!TaskBusinessRules.canDelete(task)) {
throw new Error('无法删除进行中的任务');
}
await this.repository.delete(id);
this.logger?.log(`[Service] 任务已删除: ${id}`);
}
}
// ============= 表示层(Presentation) =============
// DTO(数据传输对象)
interface CreateTaskDTO {
title: string;
description: string;
priority: 'low' | 'medium' | 'high';
}
interface UpdateTaskDTO {
title?: string;
description?: string;
status?: 'pending' | 'in-progress' | 'completed';
priority?: 'low' | 'medium' | 'high';
}
interface TaskDTO {
id: string;
title: string;
description: string;
status: string;
priority: string;
createdAt: string;
updatedAt: string;
}
// 控制器
class TaskController {
constructor(private taskService: ITaskService) {}
async getAllTasks(): Promise<{ success: boolean; data?: TaskDTO[]; error?: string }> {
try {
console.log('[Controller] GET /tasks');
const tasks = await this.taskService.getAllTasks();
return {
success: true,
data: tasks.map(t => this.taskToDTO(t))
};
} catch (error) {
console.error('[Controller] 错误:', error);
return {
success: false,
error: error.message
};
}
}
async getTask(id: string): Promise<{ success: boolean; data?: TaskDTO; error?: string }> {
try {
console.log(`[Controller] GET /tasks/${id}`);
const task = await this.taskService.getTaskById(id);
return {
success: true,
data: this.taskToDTO(task)
};
} catch (error) {
console.error('[Controller] 错误:', error);
return {
success: false,
error: error.message
};
}
}
async createTask(dto: CreateTaskDTO): Promise<{ success: boolean; data?: TaskDTO; error?: string }> {
try {
console.log('[Controller] POST /tasks');
const task = await this.taskService.createTask({
...dto,
status: 'pending'
});
return {
success: true,
data: this.taskToDTO(task)
};
} catch (error) {
console.error('[Controller] 错误:', error);
return {
success: false,
error: error.message
};
}
}
async updateTask(id: string, dto: UpdateTaskDTO): Promise<{ success: boolean; data?: TaskDTO; error?: string }> {
try {
console.log(`[Controller] PUT /tasks/${id}`);
const task = await this.taskService.updateTask(id, dto);
return {
success: true,
data: this.taskToDTO(task)
};
} catch (error) {
console.error('[Controller] 错误:', error);
return {
success: false,
error: error.message
};
}
}
async markAsCompleted(id: string): Promise<{ success: boolean; data?: TaskDTO; error?: string }> {
try {
console.log(`[Controller] POST /tasks/${id}/complete`);
const task = await this.taskService.markAsCompleted(id);
return {
success: true,
data: this.taskToDTO(task)
};
} catch (error) {
console.error('[Controller] 错误:', error);
return {
success: false,
error: error.message
};
}
}
async deleteTask(id: string): Promise<{ success: boolean; error?: string }> {
try {
console.log(`[Controller] DELETE /tasks/${id}`);
await this.taskService.deleteTask(id);
return {
success: true
};
} catch (error) {
console.error('[Controller] 错误:', error);
return {
success: false,
error: error.message
};
}
}
private taskToDTO(task: Task): TaskDTO {
return {
id: task.id,
title: task.title,
description: task.description,
status: task.status,
priority: task.priority,
createdAt: task.createdAt.toISOString(),
updatedAt: task.updatedAt.toISOString()
};
}
}
// ============= 应用组装 =============
// 日志接口
interface ILogger {
log(message: string): void;
error(message: string): void;
}
class ConsoleLogger implements ILogger {
log(message: string): void {
console.log(message);
}
error(message: string): void {
console.error(message);
}
}
// 组装应用
const db = new MockDatabase();
const repository = new TaskRepository(db);
const logger = new ConsoleLogger();
const service = new TaskService(repository, logger);
const controller = new TaskController(service);
// 使用示例
async function example() {
// 创建任务
const createResult = await controller.createTask({
title: '学习 TypeScript',
description: '深入学习 TypeScript 高级特性',
priority: 'high'
});
console.log('创建结果:', createResult);
// 获取所有任务
const allTasks = await controller.getAllTasks();
console.log('所有任务:', allTasks);
// 更新任务
if (createResult.data) {
const updateResult = await controller.updateTask(createResult.data.id, {
status: 'in-progress'
});
console.log('更新结果:', updateResult);
// 标记为完成
const completeResult = await controller.markAsCompleted(createResult.data.id);
console.log('完成结果:', completeResult);
}
}3. 错误处理策略
良好的错误处理是构建健壮应用的关键。
3.1 错误分类
typescript
// 基础错误类
class AppError extends Error {
constructor(
message: string,
public statusCode: number = 500,
public isOperational: boolean = true
) {
super(message);
Object.setPrototypeOf(this, AppError.prototype);
Error.captureStackTrace(this, this.constructor);
}
}
// 业务错误
class BusinessError extends AppError {
constructor(message: string) {
super(message, 400, true);
}
}
// 验证错误
class ValidationError extends AppError {
constructor(
message: string,
public errors: string[]
) {
super(message, 400, true);
}
}
// 未找到错误
class NotFoundError extends AppError {
constructor(resource: string) {
super(`${resource} 未找到`, 404, true);
}
}
// 认证错误
class AuthenticationError extends AppError {
constructor(message: string = '认证失败') {
super(message, 401, true);
}
}
// 授权错误
class AuthorizationError extends AppError {
constructor(message: string = '权限不足') {
super(message, 403, true);
}
}
// 数据库错误
class DatabaseError extends AppError {
constructor(message: string) {
super(message, 500, false); // 非操作性错误
}
}使用示例:
typescript
class UserService {
async getUser(id: string): Promise<User> {
// 验证输入
if (!id) {
throw new ValidationError('用户ID不能为空', ['id 是必需的']);
}
try {
const user = await this.repository.findById(id);
// 检查是否存在
if (!user) {
throw new NotFoundError('用户');
}
return user;
} catch (error) {
// 数据库错误
if (error instanceof DatabaseError) {
// 记录日志并抛出
this.logger.error(`数据库错误: ${error.message}`);
throw error;
}
// 重新抛出其他错误
throw error;
}
}
}3.2 全局错误处理器
typescript
class ErrorHandler {
private logger: ILogger;
constructor(logger: ILogger) {
this.logger = logger;
}
handleError(error: Error | AppError): {
statusCode: number;
message: string;
errors?: string[];
} {
// 判断是否为操作性错误
const isAppError = error instanceof AppError;
if (isAppError && error.isOperational) {
// 操作性错误:记录并返回给客户端
this.logger.error(
`操作性错误: ${error.message}\n` +
`状态码: ${error.statusCode}\n` +
`堆栈: ${error.stack}`
);
return {
statusCode: error.statusCode,
message: error.message,
errors: error instanceof ValidationError ? error.errors : undefined
};
} else {
// 编程错误:记录详细信息但返回通用消息
this.logger.error(
`编程错误: ${error.message}\n` +
`堆栈: ${error.stack}`
);
// 在生产环境中,不暴露内部错误详情
return {
statusCode: 500,
message: '服务器内部错误'
};
}
}
// Express 中间件形式
expressErrorHandler() {
return (err: Error, req: any, res: any, next: any) => {
const errorResponse = this.handleError(err);
res.status(errorResponse.statusCode).json(errorResponse);
};
}
}由于内容篇幅较长,我将在下一部分继续补充日志监控、配置管理等内容。
继续完成剩余章节...