038-API层架构设计
038 — API 层架构设计:从枚举定义到模块化 API 管理
简介
随着业务复杂度的增长,网络请求的管理方式直接影响项目的可维护性。MoneyTrack 采用了一套分层清晰的 API 架构:底层是单例 Axios 客户端(037 篇已述),中间层是枚举驱动的接口地址集中管理,上层是按业务域划分的模块化 API 文件。这套架构使得 30+ 个网络端点的查找、维护和调试变得井然有序,新增一个接口只需添加枚举值和对应方法,无需改动既有代码。
API 三层架构全景
核心知识点
1. API 枚举集中管理
将所有后端接口地址定义在统一的RequestUrlMap枚举中,实现接口地址的"单一数据源":
- 避免硬编码:字符串散落在各文件中容易拼写错误,枚举统一管控。
- 自文档化:枚举名即接口用途说明,一目了然。
- 类型安全:配合 TypeScript 类型检查,修改地址时全局可控。
以下是 MoneyTrack 项目中RequestUrlMap枚举的完整展示(覆盖用户、家庭、账本、账单、资产五大模块):
exportenumRequestUrlMap{/** ===== 用户相关 ===== */USER_LOGIN='user/login',USER_LOGOUT='user/logout',USER_INFO='user/info',USER_MEMBERSHIP='user/membership',/** ===== 家庭相关 ===== */FAMILY_CREATE='family/create',FAMILY_JOIN='family/join',FAMILY_MEMBERS='family/members',FAMILY_LEAVE='family/leave',FAMILY_REMOVE_MEMBER='family/removeMember',/** ===== 账本相关 ===== */ACCOUNT_BOOK_LIST='accountBook/list',ACCOUNT_BOOK_CREATE='accountBook/create',ACCOUNT_BOOK_UPDATE='accountBook/update',ACCOUNT_BOOK_DELETE='accountBook/delete',ACCOUNT_BOOK_SWITCH='accountBook/switch',/** ===== 账单相关 ===== */BILL_LIST='bill/list',/** ===== 资产相关 ===== */ASSET_LIST='asset/list',}2. 模块化 API 文件
按业务领域将 API 调用拆分到独立文件,每个文件只负责一个业务模块。采用类 + 单例导出模式,既保持了面向对象的封装性,又方便上层调用:
// Asset.ets — 资产模块 APIclassAssetApis{publicgetAssetList(ownerId?:number):Promise<BaseResponse>{constparams:Record<string,Object>={};if(ownerId!==undefined){params['ownerId']=ownerId;}returnrequest.get(RequestUrlMap.ASSET_LIST,{params});}}constinstance=newAssetApis();export{instanceasAssetApis};// User.ets — 用户模块 APIclassUserApis{publiclogin(params?:UserLoginReq):Promise<BaseResponse>{returnrequest.get(RequestUrlMap.USER_LOGIN,{params});}publiclogout():Promise<BaseResponse>{returnrequest.get(RequestUrlMap.USER_LOGOUT);}publicupdateUserInfo(data:UpdateUserInfoReq):Promise<BaseResponse>{returnrequest.put(RequestUrlMap.USER_INFO,data);}publicsubscribeMembership():Promise<BaseResponse>{returnrequest.post(RequestUrlMap.USER_MEMBERSHIP);}publicgetMembershipInfo():Promise<MembershipInfoResp>{returnrequest.get(RequestUrlMap.USER_MEMBERSHIP);}}constinstance=newUserApis();export{instanceasUserApis};3. 类型安全的泛型约束
每个 API 都明确定义了请求参数类型和响应数据类型,利用 TypeScript 泛型在编译期捕获类型错误:
// 定义泛型响应结构exportinterfaceBaseResponse<T=any>{code:number;message:string;data:T;}// 在 API 方法中使用泛型约束classBillApis{publicgetBillList(memberId?:number):Promise<BaseResponse<BillItem[]>>{constparams:Record<string,Object>={};if(memberId!==undefined){params['memberId']=memberId;}returnrequest.get(RequestUrlMap.BILL_LIST,{params});}}当后端返回的数据结构发生变化时,只需修改泛型类型定义,所有调用方都会收到编译警告,极大降低回归风险。
4. API 版本管理
URL 中携带版本号是管理 API 演进的通行做法。在request层通过 baseURL 携带主版本号,或通过拦截器自动注入版本参数:
// 方案一:baseURL 携带版本号constinstance=axios.create({baseURL:'https://api.moneytrack.com/v2/',// 整个应用使用 v2 版本});// 方案二:按模块在 API 路径中指定版本enumRequestUrlMap{USER_LOGIN_V1='v1/user/login',USER_LOGIN_V2='v2/user/login',// 渐进式升级}| 特性 | 枚举集中管理 | 硬编码字符串 |
|---|---|---|
| 可维护性 | 一处修改全局生效 | 四处查找替换 |
| 自文档化 | 枚举名说明用途 | 需额外注释 |
| 类型检查 | 编译期检测 | 运行时才能发现 |
| 协作效率 | 新人快速了解接口 | 需翻阅文档 |
最佳实践
- 枚举命名规范:采用
模块_动作格式(如USER_LOGIN、BILL_LIST),按模块分组并用注释分隔,方便快速定位。 - API 文件粒度:一个业务模块一个文件,文件内只导出类实例(单例),避免命名空间污染。
- 类型优先:先定义请求/响应的 TypeScript 接口,再实现 API 方法。让类型定义驱动开发流程。
- 渐进式版本升级:新旧版本接口枚举共存,逐个模块迁移,避免大版本一次性升级的风险。
推荐参考文档
- TypeScript 枚举与泛型文档
- RESTful API 版本管理最佳实践
