基于 GraphQL 平台化 BFF 构建及微服务治理( 三 )


在我们的方案里 , 为了方便后端同学更加快速的接入GraphQL以及兼容我们内部的服务治理框架 , 我们提供了一套Java注解的方式方便业务的同学快速构建出一个GraphQL服务出来 。
针对GraphQL里的Type、Enum、Interface、Union、Query等 , 我们定义了对应的注解进行转换 。
基于 GraphQL 平台化 BFF 构建及微服务治理
文章图片
而针对字段扩展 , 我们单独定义了一个注解来进行处理 , 可以参考如下形式:
@GraphQLFieldAttach(targetType=''Property'',sourceFields=''communityId'',targetFieldName=''community'',batch=true)publicMapResponse<Long,Community>getCommunity(@GraphQLQueryKeySet<Long>communityId);我们的房源(Property)和小区(Community)数据是属于两个不同的领域来对外提供服务的 。 实际的业务场景里 , 房源属于某一个小区 , 有个字段(communityId)保存着小区id , 因此需要将这两个数据对象进行关联 。 我们提供了一个查询小区的接口(CommunityService) , 再通过上面的注解 , 在GraphQL里绑定到房源的对象的Property.community字段上 。 这样当查询请求处理到Property.community的时候 , 会自动请求这个接口 , 获取小区数据 , 返回给调用方 。
基于 GraphQL 平台化 BFF 构建及微服务治理
文章图片
同时为了适配大家已有的微服务体系 , 这里以SpringCloud为例 , 把上面的接口定义打包成类似FeighClient这样二方包的形式 , 集成到Gateway中的依赖里 。 然后扫描Jar包自动生成Schema和DataFetcher , 在DataFetcher里调用对应的FeignClient 。 这样就可以自动构建出一个完整的GraphQL服务 。
基于 GraphQL 平台化 BFF 构建及微服务治理
文章图片
在GraphQL网关里我们会解析各个服务的二方包 , 自动生成Schema和对应的字段解析调用 。 当某个业务有需求的时候可以非常快速的集成到我们的GraphQL体系中
目前二方包的依赖还是静态管理的 , 有更新后需要重新部署网关 , 后续迭代中我们会升级支持动态更新Jar包以实现动态生成Schema的能力 。
3.3引入JSON模板
前端页面所需的JSON字段的结构和GraphQL查询结果的JSON结构往往不相同 , 而且页面上也存在一些format、if-else的判断逻辑 , 这部分放在GraphQL里的话其实很难实现 。 特别是现在的一些前端低代码平台 , 页面的展现模块可能在很多不同的页面复用 , 这样的字段定义和后端的数据字段定义是完全不一样的 , 一定需要有人参与这部分转换工作 。 参考Node.js生态的解决方案和以前后端模板的页面渲染方式 , 我们采用JSON模板来对这两个不同的JSON结构进行映射 。
目前我们的平台支持JSLT模板、Javascript两种方式来进行JSON结构的映射 , 下面以JSLT为例(JSLT是一个开源的JSON模板引擎 , 基于Java语言 , 详情可以参考JSLT) 。
//GraphQL的结果 , 模板的输入JSON{''data'':[{''id'':10000,''title'':''房子1'',''roomNum'':2,''hallNum'':2,''area'':90.12},{''id'':10001,''title'':''房子2'',''roomNum'':3,''hallNum'':2,''area'':99.34},...]}//JSLT模板{''dataList'':[for(.data){''id'':.id,''title'':.title,''label1'':''户型'',''text1'':.roomNum+''室''+.hallNum+''厅'',''label2'':''面积'',''text2'':.area+''㎡'' , ''link'':URLRoute(''HousePage'',{''id'':.id})}]}//输出JSON{''dataList'':[{''id'':10000,''title'':''房子1'',''label1'':''户型'',''text1'':''2室2厅'',''label2'':''面积'',''text2'':''90.12㎡'',''link'':''https://anjuke.com/house.html?id=10000''},{''id'':10001,''title'':''房子2'',''label1'':''户型'',''text1'':''3室2厅'',''label2'':''面积'',''text2'':''100.34㎡'',''link'':''https://anjuke.com/house.html?id=10001''}]上面这个例子可以发现 , 最终输出的JSON结构和字段名称和GraphQL请求返回的结构完全不同 。 通过这样的映射处理 , 可以完全解耦前端页面的展示逻辑和后端提供数据的取数逻辑 , 根据前端页面对返回数据的结构要求 , 我们可以进行各种JSON结构的转换来适配 。 后期随着模板越来越复杂 , 也可以引入一些可复用的子模板方式来进行管理