如何在TypeScript中分离Kcell电信API的DTO与处理逻辑?
问题描述
我有若干用于描述Kcell电信API对象的DTO类,示例如下:
/** * ... * */ export class KcellEventRequest { /** * ... * */ cmd: 'event' /** * ... * */ crm_token: string ... }
每个Kcell事件对应一个此类DTO。接收数据后,我通过序列化器将其转换为对应DTO实例:
private serializer (body: any): KcellEventRequest | KcellHistoryRequest | KcellContactRequest | KcellRatingRequest { body.authorization = this.parseToken(body.crm_token) switch (body.cmd) { case 'event' : return plainToInstance(KcellEventRequest, body) case 'history': return plainToInstance(KcellHistoryRequest, body) case 'contact': return plainToInstance(KcellContactRequest, body) case 'rating': return plainToInstance(KcellRatingRequest, body) default: throw Error('Incorrect cmd') } } async handleRequest (body: any, headers: any): Promise<any> { // serialize object const callObject = this.serializer(body) // {{read next}} return await callObject.handle(headers) }
这些DTO类均实现了TelephonyAPIHandler接口,包含handle方法以触发业务逻辑。目前我通过callObject.handle(headers)直接调用,但不希望处理逻辑与DTO耦合,请问如何将handle方法从DTO中分离?
解决方案
核心思路是把业务逻辑从DTO中抽离到独立的处理器类,通过命令-处理器映射表关联数据结构与业务行为,实现完全解耦。
步骤1:让DTO回归纯数据结构
移除所有DTO类中的handle方法,只保留字段定义,让它们仅负责承载数据:
// 纯数据DTO,仅定义数据字段 export class KcellEventRequest { cmd: 'event' crm_token: string authorization?: string // 其他业务字段... } export class KcellHistoryRequest { cmd: 'history' crm_token: string authorization?: string // 其他业务字段... } // 同理处理KcellContactRequest、KcellRatingRequest
步骤2:创建独立的处理器接口与实现类
定义统一的处理器接口,为每个DTO编写对应的业务逻辑实现:
// 处理器统一契约 interface RequestHandler<T> { handle(dto: T, headers: any): Promise<any> } // 事件请求专属处理器,实现对应业务逻辑 class KcellEventHandler implements RequestHandler<KcellEventRequest> { async handle(dto: KcellEventRequest, headers: any): Promise<any> { // 这里写原KcellEventRequest.handle中的业务逻辑 // 比如调用服务、校验参数、返回响应等 return { code: 200, msg: '事件处理完成', data: {} } } } // 历史请求专属处理器 class KcellHistoryHandler implements RequestHandler<KcellHistoryRequest> { async handle(dto: KcellHistoryRequest, headers: any): Promise<any> { // 历史查询业务逻辑 return { code: 200, msg: '历史数据查询完成', data: [] } } } // 同理实现KcellContactHandler、KcellRatingHandler
步骤3:建立命令与处理器的映射关系
用映射表替代原有的switch逻辑,后续新增命令只需扩展映射表即可:
// 映射表:cmd -> DTO类 + 对应处理器实例 const handlerMap: Record<string, { dtoClass: any, handler: RequestHandler<any> }> = { 'event': { dtoClass: KcellEventRequest, handler: new KcellEventHandler() }, 'history': { dtoClass: KcellHistoryRequest, handler: new KcellHistoryHandler() }, 'contact': { dtoClass: KcellContactRequest, handler: new KcellContactHandler() }, 'rating': { dtoClass: KcellRatingRequest, handler: new KcellRatingHandler() } }
步骤4:修改请求处理流程
更新序列化与请求处理逻辑,通过映射表匹配处理器执行业务逻辑:
private getProcessEntry(body: any): { dto: any, handler: RequestHandler<any> } { body.authorization = this.parseToken(body.crm_token) const cmd = body.cmd as string const entry = handlerMap[cmd] if (!entry) { throw new Error('Incorrect cmd') } const dto = plainToInstance(entry.dtoClass, body) return { dto, handler: entry.handler } } async handleRequest(body: any, headers: any): Promise<any> { const { dto, handler } = this.getProcessEntry(body) return await handler.handle(dto, headers) }
方案优势
- 职责单一:DTO仅负责数据结构,处理器仅负责业务逻辑,便于维护和分工
- 扩展性强:新增命令时,只需添加DTO类、处理器类,更新映射表即可,无需修改原有代码
- 可测试性高:处理器可以单独进行单元测试,无需依赖DTO实例化逻辑
内容的提问来源于stack exchange,提问作者vitaliyJR
相关产品推荐
相关产品推荐

