1 BFF的由来
2 BFF背景下的核心矛盾
3 BFF应用模式分析
3.1 后端BFF模式
3.2 前端BFF模式
4 基于GraphQL及元数据的信息聚合架构设计
4.1 整体思路
4.2 核心设计
5 针对GraphQL的优化实践
5.1 使用简化
5.2 性能优化
6 新架构对研发模式的影响
6.1 聚焦业务的开发模式
6.2 研发流程升级
7 总结
8 参考文献
9 招聘信息
1 BFF的由来
BFF一词来自Sam Newman的一篇博文《Pattern:Backends For Frontends》,指的是服务于前端的后端。BFF是解决什么问题的呢?据原文描述,随着移动互联网的兴起,原适应于桌面Web的服务端功能希望同时提供给移动App使用,而在这个过程中存在这样的问题:
移动App和桌面Web在UI部分存在差异。
移动App涉及不同的端,不仅有iOS、还有Android,这些不同端的UI之间存在差异。
原有后端功能和桌面Web UI之间已经存在了较大的耦合。
因为端的差异性存在,服务端的功能要针对端的差异进行适配和裁剪,而服务端的业务功能本身是相对单一的,这就产生了一个矛盾——服务端的单一业务功能和端的差异性诉求之间的矛盾。那么这个问题怎么解决呢?这也是文章的副标题所描述的"Single-purpose Edge Services for UIs and external parties",引入BFF,由BFF来针对多端差异做适配,这也是目前业界广泛使用的一种模式。
图1 BFF示意图
在实际业务的实践中,导致这种端差异性的原因有很多,有技术的原因,也有业务的原因。比如,用户的客户端是Android还是iOS,是大屏还是小屏,是什么版本。再比如,业务属于哪个行业,产品形态是什么,功能投放在什么场景,面向的用户群体是谁等等。这些因素都会带来面向端的功能逻辑的差异性。
在这个问题上,笔者所在团队负责的商品展示业务有一定的发言权,同样的商品业务,在C端的展示功能逻辑,深刻受到商品类型、所在行业、交易形态、投放场所、面向群体等因素的影响。同时,面向消费者端的功能频繁迭代的属性,更是加剧并深化了这种矛盾,使其演化成了一种服务端单一稳定与端的差异灵活之间的矛盾,这也是商品展示(商品展示BFF)业务系统存在的必然性原因。本文主要在美团到店商品展示场景的背景下,介绍面临的一些问题及解决思路。
BFF这层的引入是解决服务端单一稳定与端的差异灵活诉求之间的矛盾,这个矛盾并不是不存在,而是转移了。由原来后端和前端之间的矛盾转移成了BFF和前端之间的矛盾。笔者所在团队的主要工作,就是和这种矛盾作斗争。下面以具体的业务场景为例,结合当前的业务特点,说明在BFF的生产模式下,我们所面临的具体问题。下图是两个不同行业的团购货架展示模块,这两个模块我们认为是两个商品的展示场景,它们是两套独立定义的产品逻辑,并且会各自迭代。
图2 展示场景
在业务发展初期,这样的场景不多。BFF层系统“烟囱式”建设,功能快速开发上线满足业务的诉求,在这样的情况下,这种矛盾表现的不明显。而随着业务发展,行业的开拓,形成了许许多多这样的商品展示功能,矛盾逐渐加剧,主要表现在以下两个方面:
业务支撑效率:随着商品展示场景变得越来越多,API呈爆炸趋势,业务支撑效率和人力成线性关系,系统能力难以支撑业务场景的规模化拓展。
系统复杂度高:核心功能持续迭代,内部逻辑充斥着if…else…
,代码过程式编写,系统复杂度较高,难以修改和维护。
那么这些问题是怎么产生的呢?这要结合“烟囱式”系统建设的背景和商品展示场景所面临的业务,以及系统特点来进行理解。
特点一:外部依赖多、场景间取数存在差异、用户体验要求高
图例展示了两个不同行业的团购货架模块,这样一个看似不大的模块,后端在BFF层要调用20个以上的下游服务才能把数据拿全,这是其一。在上面两个不同的场景中,需要的数据源集合存在差异,而且这种差异普遍存在,这是其二,比如足疗团购货架需要的某个数据源,在丽人团购货架上不需要,丽人团购货架需要的某个数据源,足疗团购货架不需要。尽管依赖下游服务多,同时还要保证C端的用户体验,这是其三。
这几个特点给技术带来了不小的难题:1)聚合大小难控制,聚合功能是分场景建设?还是统一建设?如果分场景建设,必然存在不同场景重复编写类似聚合逻辑的问题。如果统一建设,那么一个大而全的数据聚合中必然会存在无效的调用。2)聚合逻辑的复杂性控制问题,在这么多的数据源的情况下,不仅要考虑业务逻辑怎么写,还要考虑异步调用的编排,在代码复杂度未能良好控制的情况下,后续聚合的变更修改将会是一个难题。
特点二:展示逻辑多、场景之间存在差异,共性个性逻辑耦合
我们可以明显地识别某一类场景的逻辑是存在共性的,比如团单相关的展示场景。直观可以看出基本上都是展示团单维度的信息,但这只是表象。实际上在模块的生成过程中存在诸多的差异,比如以下两种差异:
字段拼接逻辑差异:比如以上图中两个团购货架的团购标题为例,同样是标题,在丽人团购货架中的展示规则是:[类型] + 团购标题,而在足疗团购货架的展示规则是:团购标题。
排序过滤逻辑差异:比如同样是团单列表,A场景按照销量倒排序,B场景按照价格排序,不同场景的排序逻辑不同。
诸如此类的展示逻辑的差异性还有很多。类似的场景实际上在内部存在很多差异的逻辑,后端如何应对这种差异性是一个难题,下面是最常见的一种写法,通过读取具体的条件字段来做判断实现逻辑路由,如下所示:
if(category == "丽人") {
title = "[" + category + "]" + productTitle;
} else if (category == "足疗") {
title = productTitle;
}
这种方案在功能实现方面没有问题,也能够复用共同的逻辑。但是实际上在场景非常多的情况下,将会有非常多的差异性判断逻辑叠加在一起,功能一直会被持续迭代的情况下,可以想象,系统将会变得越来越复杂,越来越难以修改和维护。
总结:在BFF这层,不同商品展示场景存在差异。在业务发展初期,系统通过独立建设的方式支持业务快速试错,在这种情况下,业务差异性带来的问题不明显。而随着业务的不断发展,需要搭建及运营的场景越来越多,呈规模化趋势。此时,业务对技术效率提出了更高的要求。在这种场景多、场景间存在差异的背景下,如何满足场景拓展效率同时能够控制系统的复杂性,就是我们业务场景中面临的核心问题。
目前,业界针对此类的解决方案主要有两种模式,一种是后端BFF模式;另一种是前端BFF模式。
后端BFF模式指的是BFF由后端同学负责,这种模式目前最广泛的实践是基于GraphQL搭建的后端BFF方案,具体是:后端将展示字段封装成展示服务,通过GraphQL编排之后暴露给前端使用。如下图所示:
图3 后端BFF模式
这种模式最大的特性和优势是,当展示字段已经存在的情况下,后端不需要关心前端差异性需求,按需查询的能力由GraphQL支持。这个特性可以很好地应对不同场景存在展示字段差异性这个问题,前端直接基于GraphQL按需查询数据即可,后端不需要变更。同时,借助GraphQL的编排和聚合查询能力,后端可以将逻辑分解在不同的展示服务中,因此在一定程度上能够化解BFF这层的复杂性。
但是基于这种模式,仍然存在几个问题:展示服务颗粒度问题、数据图划分问题以及字段扩散问题,下图是基于当前模式的具体案例: