logo
火山博客
导航

Nest数据处理模式与架构设计指南

2025-01-14
4阅读时间5分钟

前言

在构建 NestJS 应用时,合理的数据处理模式和架构设计至关重要。本文将介绍除了常见的 DTO 模式外,其他核心的设计模式和概念,帮助你构建更优雅的应用程序。

核心概念

1. 数据对象模式

1.1 Business Object (BO):

  • 定义:封装业务逻辑和数据的对象
  • 用途:
    • 实现核心业务规则
    • 处理业务流程
    • 协调领域对象
  • 示例代码:
Typescript
// user.bo.ts
export class UserBO {
    constructor(private readonly user: User) {}

    async promoteToVIP(): Promise<void> {
        if (this.canBePromoted()) {
            this.user.role = 'VIP';
            this.user.promotedAt = new Date();
        }
        throw new BusinessError('User cannot be promoted');
    }

    private canBePromoted(): boolean {
        return this.user.purchaseAmount > 1000;
    }
}

1.2 Request Object (RO):

  • 定义:基于值而非引用的不可变对象
  • 特点:
    • 属性不可修改
    • 相同值即视为相同对象
    • 无副作用
  • 示例代码:
Typescript
// money.vo.ts
export class Money {
    private constructor(
        private readonly amount: number,
        private readonly currency: string
    ) {}

    static of(amount: number, currency: string): Money {
        return new Money(amount, currency);
    }

    add(other: Money): Money {
        if (this.currency !== other.currency) {
            throw new Error('Cannot add different currencies');
        }
        return new Money(this.amount + other.amount, this.currency);
    }
}

2. 请求响应处理

2.1 Request/Response Objects (RO):

  • Request Object:
    • 封装请求参数
    • 提供请求验证
    • 简化参数传递
  • Response Object:
    • 统一响应格式
    • 处理错误信息
    • 支持数据转换
Typescript
// user.request.ts
export class CreateUserRequest {
    readonly username: string;
    readonly email: string;
    readonly password: string;

    validate(): void {
        // 验证逻辑
    }
}

// api.response.ts
export class ApiResponse<T> {
    constructor(
        public readonly success: boolean,
        public readonly data?: T,
        public readonly error?: string
    ) {}

    static success<T>(data: T): ApiResponse<T> {
        return new ApiResponse(true, data);
    }

    static error<T>(message: string): ApiResponse<T> {
        return new ApiResponse(false, undefined, message);
    }
}

3. 架构模式

3.1 Command and Query Responsibility Segregation (CQRS):

  • 命令:修改数据的操作
  • 查询:读取数据的操作
  • 优势:
    • 提高性能
    • 简化复杂度
    • 支持扩展
3.1.1 CQRS 与数据库分离策略
  • 读写分离设计:
    • Command 操作使用主库(写库)
    • Query 操作使用从库(读库)
    • 支持不同数据库类型组合
Typescript
// database.config.ts
export const databaseConfig = {
  command: {
    type: 'mysql',
    host: 'master-db.example.com',
    // ... 主库配置
  },
  query: {
    type: 'mongodb', // 可以使用不同类型数据库
    host: 'replica-db.example.com',
    // ... 从库配置
  }
};

// user.command.ts
@CommandHandler(CreateUserCommand)
export class CreateUserHandler {
    constructor(
        @InjectRepository(User, 'command')  // 注入主库连接
        private userRepo: Repository<User>
    ) {}

    async execute(command: CreateUserCommand): Promise<void> {
        // 写操作使用主库
        await this.userRepo.save(command.user);
    }
}

// user.query.ts
@QueryHandler(GetUserQuery)
export class GetUserHandler {
    constructor(
        @InjectRepository(User, 'query')  // 注入从库连接
        private userRepo: Repository<User>
    ) {}

    async execute(query: GetUserQuery): Promise<UserDTO> {
        // 读操作使用从库
        return this.userRepo.findOne(query.id);
    }
}
3.1.2 CQRS 数据同步策略
  • 同步方案:
    • 主从复制(MySQL, PostgreSQL)
    • 事件溯源(Event Sourcing)
    • 消息队列(Message Queue)
Typescript
// event-sourcing.service.ts
@Injectable()
export class EventSourcingService {
    constructor(
        private eventStore: EventStore,
        private readonly queryDb: QueryDatabase
    ) {}

    async publishEvent(event: DomainEvent): Promise<void> {
        // 1. 保存事件到事件存储
        await this.eventStore.save(event);
        
        // 2. 更新查询数据库
        await this.queryDb.handleEvent(event);
    }
}

// user.command.ts
@CommandHandler(CreateUserCommand)
export class CreateUserHandler {
    constructor(
        private eventSourcing: EventSourcingService
    ) {}

    async execute(command: CreateUserCommand): Promise<void> {
        // 创建用户事件
        const event = new UserCreatedEvent(command.user);
        
        // 发布事件进行数据同步
        await this.eventSourcing.publishEvent(event);
    }
}
3.1.3 CQRS 优势与使用场景
  • 适用场景:

    • 读写比例失衡(读多写少)
    • 复杂的报表查询需求
    • 需要高可用性的系统
  • 性能优势:

    • 读写分离提升并发性能
    • 可针对查询优化数据结构
    • 支持独立扩展读写服务
  • 架构优势:

    • 职责分离更清晰
    • 便于维护和重构
    • 支持异构数据存储

4. 管道与拦截器

4.1 Pipes

  • 数据转换
  • 参数验证
  • 类型转换
Typescript
// validation.pipe.ts
@Injectable()
export class CustomValidationPipe implements PipeTransform {
    transform(value: any, metadata: ArgumentMetadata) {
        // 自定义验证逻辑
        return value;
    }
}

4.2 Interceptors

  • 请求日志
  • 响应转换
  • 缓存处理
Typescript
// logging.interceptor.ts
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
    intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
        const now = Date.now();
        return next.handle().pipe(
            tap(() => console.log(`Request took ${Date.now() - now}ms`))
        );
    }
}

最佳实践

    1. 分层设计
    • 控制器层:处理 HTTP 请求
    • 服务层:实现业务逻辑
    • 仓储层:数据持久化
    1. 数据验证
    • 使用 DTOs 进行请求验证
    • 实现自定义验证管道
    • 统一错误处理
    1. 性能优化
    • 实现缓存策略
    • 使用 CQRS 分离读写
    • 优化数据库查询
2024 © Powered by
hsBlog
|
后台管理