BFF
2023/09/07
接口数据结构的声明
在使用TypeScript的声明后台的所给的接口数据的时候,因后台是外部因素,前端无法直接控制,接口字段是否都用Partial修饰,变为可选?
这个问题,直接的考虑:
-
全部声明为可行,原因是对后台数据不信任,即使声明为必选,也有可能由于修改等等原因,必选字段实际并没有下发。但这样处理,前端使用的时候,在业务逻辑里面,就需判断字段是否存在或者使用
?.
,使用起来很不方便,代码的噪音也很多。 -
声明为必选,使用的时候,数据有后台保证,这样省去了判断的代码。但问题也很明显,一旦后台数据有问题,很可能就会引起线上问题。
两种办法都有问题。是否有更好的办法呢?
外部系统不可信原则
上面接口数据结构的分析,都没有跳出前端和后台耦合到一起的思维。
实际项目中,应该遵循外部系统不可信原则。前端作为相对独立的子系统,任何外部的数据,都不可信。
因此,在接收的时候,就需要做好必要的校验。有异常数据,做好上报告警。并跟进数据的异常,来判断前端此时是否继续运行。
既然后天所给的接口数据不可信任,我们就可以有一下的考虑:
- 所有的外部接口协议,所有字段均可能为空;
- 做好数据校验,有异常,则上报;并以此为依据,推动后台优化;
- 前端获取外部数据以后,不直接使用外部数据,而是转换为前端自定义的数据结构。可以使用贫血模型interface,或者充血模型的class。
数据接入层(充血模型的class)
更进一步,我们考虑充血模型的class的细节和优点。
前端页面在设计的时候,很容易犯一个错误,缺乏统一的数据接入层,也缺乏对应的Model。直接使用后台接口返回的所有数据字段。或者直接从页面组件View中发起请求,更新View的内容。
直接使用API返回数据,后端的Model已经侵入到前端页面。无形中,对具体的后台有依赖。
因此,在一个应用中,对于第三方I/O(Http,JsBridge等),应该放到单独的Service中。View无需关注Service的实现,只关注Service的接口即可。这样就隔离了第三方I/O的影响。
这个Service还可以支持:
- 校验数据;失败,则抛出异常,展示兜底页面;
- 设置默认值;
- 格式化数据:获取返回的数据以后,格式化数据,将后台的数据,映射为前端所需的数据结构;
- 作为adapter:支持多个不同的后台,特别是后台数据因种种原因无法保持一致的时候,在Service中可以统一格式;
- mock数据:在开发、调试或自动化测试阶段,也可以mock数据,解耦前后端依赖;
// 前端数据协议
type DataType = {
attribute: string
};
// 后台定义的数据协议
type HttpDataType = {
httpAttribute: string
};
// 默认值
const DEFAULT_DATA: DataType = {
attribute: ''
};
class DataConversionService implements DataType {
private pAttribute: DataType['attribute'] = DEFAULT_DATA.attribute;
constructor() {}
// 数据校验
public hasAttribute(obj: unknown): obj is HttpDataType {
return (obj as HttpDataType)?.httpAttribute !== undefined
&& typeof (obj as HttpDataType).httpAttribute === "string";
}
public convert(data: unknown) {
if (this.hasAttribute(data)) {
this.pAttribute = data?.httpAttribute;
} else {
// 日志
console.log('缺少关键数据');
// 上报
// 抛出错误
// 走兜底等等
}
}
public get attribute(): DataType['attribute'] {
if (this.pAttribute) {
return this.pAttribute;
} else {
return DEFAULT_DATA.attribute;
}
}
public set attribute(obj: unknown) {
this.pAttribute = obj as DataType['attribute'];
}
}
数据接入层的Service,隔离了对外的数据的依赖,只要数据通过数据接入的校验、格式化等,View中就可以放心的使用Service中的数据,不用在每一个用到的地方再次校验。
同时,有了这个统一的数据接入层,在页面加载的时候,可以很方便的处理页面的Loading、Success和Failed状态。
BFF
阅读资料
-
Backend for frontend (BFF) pattern— why do you need to know it?
-
前端開發者該負責寫 API Endpoints 嗎?The Backend For Frontend Pattern (BFF) In Microservices World
log
- 2023/09/07 初稿