微服务囧途之BFF层登场
从单体架构演化为微服务架构后,架构者的期望是“模块A”+“模块B” = “后端服务”。
场景一
Web端和Mobile端都有一个详情页面,需要调用模块A的getDetail接口获取数据。假设Web端实际需要展示的字段是20个,Mobile端实际需要展示的字段是10个,对应的数据表字段为30个。
方案一:
为Web端和Mobile端提供不同的接口返回各自实际所需的字段。如:getDetailForWeb、getDetailForMobile;
方案二:
Web端和Mobile端复用同一个getDetail接口,返回字段取两端所需的并集;
方案三:
Web端和Mobile端复用同一个getDetail接口,返回数据表中的所有字段,各端自取所需,以后业务上可能会再出来小程序端、xx端等则可“复用”,可“扩展”;
方案一的囧境:
- 模块A承担了多端业务变化的适配工作,端上的业务调整都会引起模块A的变化;
方案二、方案三的囧境:
- 接口返回太多无用字段导致接口响应变慢并增加了前端同学理解接口的难度;
- 因为接口被多端使用会给后续维护同学增加重构的心理压力,结果就是随着业务的发展接口返回字段变得越来越多;
场景二
前端有个页面需要同时展示模块A接口和模块B接口的数据。
方案一:
前端分别调用模块A和模块B的接口并展示;
方案二:
前端调用模块A接口,模块A调用模块B获取数据并组装后返回给前端;
方案一的囧境:
- 当页面依赖的模块不多时不会有什么问题,当依赖的模块很多时会影响响应(浏览器对同一域名有最大请求并发限制);
方案二的囧境:
- 模块A和模块B耦合在了一起,并很可能出现双向依赖的情况;
- 模块A和模块B之间的边界开始模糊;
场景三
前端有个页面需要根据模块B getType接口的返回,来筛选展示模块A getDetail返回的内容。
方案一:
前端分别调用模块A和模块B的接口,然后根据type来筛选展示;
方案二:
前端只调用模块A getDetail接口,模块A去调用模块B getType接口并过滤数据返回给前端;
方案一的囧境:
- 长期发展会导致前端逻辑越来越重,还可能出现同一个功能逻辑散落在前端和后端;
方案二的囧境:
- 同场景二的方案二;
优化方案
增加BFF(Backend For Frontend)层,这样职责就清晰了。
前端:专注于交互、展示;
BFF层:专注于为各端提供接口服务;
能力层(模块A、模块B):专注于领域内的事情;