中台是个什么鬼 | 白话中台战略

从去年开始,好像就有一只无形的手一直将我与“微服务”、“平台化”、“中台化”撮合在一起,给我带来了很多的困扰和思考与收获。

故事的开始源于去年的技术雷达峰会,我在会上做了一场关于平台崛起的主题分享(《The Rise of Platform》),这场分享主要是从技术的层面从Global的视角介绍了平台化的兴起,以及分享从基础设施到人工智能等各个领域不断涌现的各类平台,以及平台化对于软件开发人员及企业的影响。

(2017技术雷达峰会)

记得当时在做演讲彩排的时候,有同事就提到过,在中国提“数字化平台战略”可能大家会觉得比较抽象比较远大空,如果你提“中台”大家会更熟悉一些。

而这也是我第一次听到“中台”这个词,原来除了我们熟悉的“前台”和“后台”外,居然还有个“中台”这样一个神奇的存在。

那…… 中台到底是什么?会不会又是另一个Buzzword呢?这个从名字上看像是从前台与后台中间硬挤出来的新断层,它与前台和后台的区别和界限到底在哪儿?什么应该放到中台,什么又应该放到前台或是后台?它的出现到底是为了解决什么问题呢?

从那时开始,一个接一个的问题就不断的涌出并萦绕在我的脑子里。直到一年多后的今天,随着参与的几个平台化、企业中台相关的项目已经顺利地步上了正轨,终于可以坐下来回顾一下这一年的实践与思考,再次试图回答这些问题,并梳理成文,与大家交流探讨。

中台迷思

到处都在喊中台,到处都是中台,中台这个词在我看来已经被滥用了。

  • 在有些人眼里:中台就是技术平台,像微服务开发框架、Devops平台、PaaS平台,容器云之类的,人们都叫它“技术中台”。
  • 在有些人眼里:中台就是微服务业务平台,像最常见的什么用户中心,订单中心,各种微服务集散地,人们都叫它“业务中台”。
  • 在有些人眼里:中台应该是组织的事情,在释放潜能:平台型组织的进化路线图 (豆瓣)中就提出了平台型组织和组织中台的概念,这类组织中台在企业中主要起到投资评估与投后管理的作用,类似于企业内部资源调度中心和内部创新孵化组织,人们叫它“组织中台”

看完本篇你就会理解,上边的这几类“中台”划分还是靠谱的,更多我看到的情况是大家为了响应企业的“中台战略”,干脆直接将自己系统的“后端”或是“后台”改个名,就叫“中台”。

中台到底是什么?它对于企业的意义到底是什么?当我们谈中台时我们到底在谈些什么?

想要寻找到答案,仅仅沉寂在各自“中台”之中,如同管中窥豹,身入迷阵,是很难想清楚的。不如换个 ⾓度,从各类的“中台迷阵”中跳脱出来,尝试以更高的视角,从企业均衡可持续发展的角度,来思考中台的价值,来试图反推它存在的价值。

所以,为了搞明白中台存在的价值,我们需要回答以下两个问题:

  1. 企业为什么要平台化?
  2. 企业为什么要建中台?

第一个问题:企业为什么要平台化?

先给答案,其实很简单:

因为在当今互联网时代,⽤户才是商业战场的中心,为了快速响应用户的需求,借助平台化的力量可以事半功倍。

不断快速响应、探索、挖掘、引领⽤户的需求,才是企业得以⽣存和持续发展的关键因素。

那些真正尊重用户,甚⾄不惜调整⾃己颠覆⾃己来响应⽤户的企业将在这场以⽤户为中心的商业战争中得以⽣存和发展;⽽反之,那些在过去的成就上故步⾃封,存在侥幸⼼理希望⽤户会像之前一样继续追随⾃己的企业则会被用户淘汰。

很残酷,但这就是这个时代最基本的企业⽣存法则

数字化企业

⽽平台化之所以重要,就是因为它赋予或加强了企业在以用户为中心的现代商业战争中最最最核心的能力:⽤户响应力。这种能力可以帮助企业在商战上先发制⼈,始终抢得先机。

可以说,在互联网时代,商业的斗争就是对于用户响应力的比拼。

又有点远大空是不是,我们来看⼏个经典的例子:

1.说起中台,最先想到的应该就属是阿⾥的“⼤中台,⼩前台”战略。阿⾥⼈通过多年不懈的努力,在业务的不断催化滋养下,将⾃己的技术和业务能力沉淀出一套综合能力平台,具备了对于前台业务变化及创新的快速响应能力。

(阿里中台)

2.海尔也早在⼗年前就已经开始推进平台化组织的转型,提出了“平台⾃营体⽀撑⼀线⾃营体”的战略规划和转型⽬标。构建了“⼈单合一”、“⽤户付薪” 的创客文化,真正将平台化提⾼到了组织的⾼度。

(海尔组织中台)

3.华为在几年前就提出了“⼤平台炮火支撑精兵作战”的企业战略,“让听得到炮声的人能呼唤到炮火” 这句话形象的诠释了大平台⽀撑下小前台的作战策略。这种极度灵活又威力巨⼤的战法,使之可以迅速响应瞬息万变的战场,一旦锁定目标,通过大平台的炮火群,迅速精准对于战场进行强大的火⼒支援。

(⼤平台炮火支撑精兵作战)

可⻅,在互联⽹热火朝天,第四次工业革命的曙光即将到来的今日,企业能否真正做到“以用户为中心”,并不断提升自己的用户响应力来追随甚至引领用户的脚步,持续规模化创新,终将决定企业能否在这样充满挑战和机遇的市场上笑到最后,在商业上长久保持创新活力与竞争力。

(数字化平台战略)

而平台化恰好可以助力企业更快更好的做到这些,所以这回答了第一个问题,企业需要平台化。

第二个问题:企业为什么要建中台?

好,想明白了第一个问题,为什么需要平台化。但是平台化并不是一个新概念,很多企业在这个方向上已经做了多年的努力和积淀。那为什么最近几年“中台”这个相对较新的概念又会异军突起?对于企业来讲,传统的“前台+后台”的平台化架构又为什么不能满足企业的要求呢?

好,这就引出了我们的第二个问题:企业为什么要建中台?

来,先定义一下前台与后台

因为平台这个词过于宽泛了,为了能让大家理解我在说什么,我先定义一下本篇文章上下文下我所说的前台和后台各指什么:

  • 前台:由各类前台系统组成的前端平台。每个前台系统就是一个用户触点,即企业的最终用户直接使用或交互的系统,是企业与最终用户的交点。例如用户直接使用的网站,手机App,微信公众号等都属于前台范畴。
  • 后台:由后台系统组成的后端平台。每个后台系统一般管理了企业的一类核心资源(数据+计算),例如财务系统,产品系统,客户管理系统,仓库物流管理系统等,这类系统构成了企业的后台。基础设施和计算平台作为企业的核心计算资源,也属于后台的一部分。

后台并不为前台而生

定义了前台和后台,对于第二个问题(企业为什么要建中台),同样先给出我的答案:

因为企业后台往往并不能很好的支撑前台快速创新响应用户的需求,后台更多解决的是企业管理效率问题,而中台要解决的才是前台的创新问题

大多数企业已有的后台,要么前台根本就用不了,要么不好用,要么变更速度跟不上前台的节奏。

我们看到的很多企业的后台系统,在创建之初的目标,并不是主要服务于前台系统创新,而更多的是为了实现后端资源的电子化管理,解决企业管理的效率问题。这类系统要不就是当年花大价钱外购,需要每年支付大量的服务费,并且版本老旧,定制化困难;要不就是花大价钱自建,年久失修,一身的补丁,同样变更困难,也是企业所谓的“遗留系统”的重灾区。

总结下来就两个字“慢”和“贵”,对业务的响应慢,动不动改个小功能就还要花一大笔钱。

有人会说了,你不能拿遗留系统说事儿啊,我们可以新建后台系统啊,整个2.0问题不就解决了。

但就算是新建的后台系统,因为其管理的是企业的关键核心数据,考虑到企业安全、审计、合规、法律等限制。导致其同样往往⽆法被前台系统直接使用,或是受到各类限制⽆法快速变化,以⽀持前台快速的创新需求。

此时的前台和后台就像是两个不同转速的⻮轮,前台由于要快速响应前端用户的需求,讲究的是快速创新迭代,所以要求转速越快越好;⽽后台由于⾯对的是相对稳定的后端资源,⽽且往系统陈旧复杂,甚至还受到法律法规审计等相关合规约束,所以往往是稳定至上,越稳定越好, 转速也自然是越慢越好。

所以,随着企业务的不断发展,这种“前台+后台”的⻮轮速率“匹配失衡”的问题就逐步显现出来。

随着企业业务的发展壮大,因为后台修改的成本和⻛险较⾼,所以驱使我们会尽量选择保持后台系统的稳定性,但还要响应用户持续不断的需求,自然就会将大量的业务逻辑(业务能力)直接塞到了前台系统中,引入重复的同时还会致使前台系统不断膨胀,变得臃肿,形成了一个个⼤泥球的“烟囱式单体应用”。渐渐拖垮了前台系统的“⽤户响应⼒”,用户满意度降低,企业竞争力也随之不断下降。

对于这样的问题,Gatner在2016年提出的一份《Pace-Layered Application Strategy》报告中,给出了一种解决方案,即按照“步速”将企业的应用系统划分为三个层次(正好契合前中后台的三个层次),不同的层次采用完全不同的策略。

而Pace-Layered Application Strategy也为“中台”产生的必然性,提供了理论上的支撑。

Pace-Layered Application Strategy

(Gatner: Pace-Layered Application Strategy)

在这份报告中Gatner提出,企业构建的系统从Pace-Layered的⾓度来看可以划分为三类: SOR(Systems of record ),SOD(Systems of differentiation)和SOI(Systems of innovation)。

处于不同Pace-Layered的系统因为⽬的不同,关注点不同,要求不同,变化的“速率”自然也不同,匹配的也需要采⽤不同的技术架构,管理流程,治理架构甚至投资策略。

⽽前面章节我们提到的后台系统,例如CRM、ERP、财务系统等,它们⼤多都处于SOR的Pace-Layered。这些系统的建设之初往往是以规范处理企业底层资源和企业的核⼼可追溯单据(例如财务单据,订单单据)为主要⽬的。它们的变更周期往往比较⻓,⽽且由于法律律审计等其他限制,导致对于它们的变更需要严谨的申报审批流程和更高级别的测试部署要求,这就导致了它们往往变化频率低,变化成本高,变化⻛险高,变化周期⻓。⽆法满⾜由⽤户驱动的快速变化的前台系统要求。

我们又要尽力保持后台(SOR)系统的稳定可靠,⼜要前台系统(SOI)能够⼩而美,快速迭代。就出现了上文提到的”齿轮匹配失衡“的问题,感觉鱼与熊掌不可兼得。

正当陷入僵局的时候,天空中飘来一声IT谚语:

软件开发中遇到的所有问题,都可以通过增加⼀层抽象⽽得以解决!

⾄此,⼀声惊雷滚过,“中台”脚踏七彩祥云,承载着SOD(Systems of differentiation) 的前世寄托,横空出世。

中台才是为前台而生

我们先试着给中台下个定义:

中台是真正为前台而生的平台(可以是技术平台,业务能力甚至是组织机构),它存在的唯一目的就是更好的服务前台规模化创新,进而更好的响应服务引领用户,使企业真正做到自身能力与用户需求的持续对接。

中台就像是在前台与后台之间添加的⼀组“变速⻮轮”,将前台与后台的速率进行匹配,是前台与后台的桥梁。它为前台而生,易于前台使用,将后台资源顺滑流向用户,响应用户。

中台很像Pace-Layered中的SOD,提供了比前台(SOI)更强的稳定性,以及⽐后台(SOR)更高的灵活性,在稳定与灵活之间寻找到了⼀种美妙的平衡。

中台作为变速齿轮,链接了用户与企业核心资源,并解决了配速问题

有了“中台”这⼀新的Pace-Layered断层,我们即可以将早已臃肿不堪的前台系统中的稳定通用业务能力“沉降”到中台层,为前台减肥,恢复前台的响应⼒;又可以将后台系统中需要频繁变化或是需要被前台直接使用的业务能力“提取”到中台层,赋予这些业务能力更强的灵活度和更低的变更成本,从而为前台提供更强大的“能力炮火”⽀援。

所以,企业在平台化的过程中,需要建设自己的中台层(同时包括技术中台,业务中台和组织中台)。

总结

思考并回答了文初提出的两个关于中台价值的核心问题,解决了我对于中台产生的一些困惑,不知道对你有没有启发,让我最后再来总结一下:

  • 以用户为中心的持续规模化创新,是中台建设的核心目标。企业的业务响应能⼒和规模化创新能力,是互联⽹时代企业综合竞争⼒的核⼼体现。平台化包括中台化只是帮助企业达到这个目标的⼿段,并不是⽬标本身。
  • 中台(⽆论是技术中台、业务中台还是组织中台)的建设根本上是为了解决企业响应⼒困境, 弥补创新驱动快速变化的前台和稳定可靠驱动变化周期相对较慢的后台之间的⽭盾,提供⼀个中间层来适配前台与后台的配速问题,沉淀能⼒,打通并顺滑链接前台需求与后台资源,帮助企业不断提升用户响应⼒。
  • 所以,中台到底是什么根本不重要,如何想方设法持续提高企业对于⽤户的响应⼒才是最重要的。⽽平台化或是中台化,只是恰巧走在了了这条正确的⼤道上。

PS: 本文是《白话中台战略》的第一篇,后续还打算继续写,总结分享介绍从问题到解决方案,从技术到业务到组织的一些实践经验。会涉及到企业技术中台规划,业务中台规划,遗留系统微服务化改造落地一系列内容,先立个Flag,希望大家看后能给一些反馈,谢谢。


更多精彩洞见,请关注微信公众号:思特沃克

Share

常用的几种大数据架构剖析

数据分析工作虽然隐藏在业务系统背后,但是具有非常重要的作用,数据分析的结果对决策、业务发展有着举足轻重的作用。随着大数据技术的发展,数据挖掘、数据探索等专有名词曝光度越来越高,但是在类似于Hadoop系列的大数据分析系统大行其道之前,数据分析工作已经经历了长足的发展,尤其是以BI系统为主的数据分析,已经有了非常成熟和稳定的技术方案和生态系统,对于BI系统来说,大概的架构图如下:

可以看到在BI系统里面,核心的模块是Cube,Cube是一个更高层的业务模型抽象,在Cube之上可以进行多种操作,例如上钻、下钻、切片等操作。大部分BI系统都基于关系型数据库,关系型数据库使用SQL语句进行操作,但是SQL在多维操作和分析的表示能力上相对较弱,所以Cube有自己独有的查询语言MDX,MDX表达式具有更强的多维表现能力,所以以Cube为核心的分析系统基本占据着数据统计分析的半壁江山,大多数的数据库服务厂商直接提供了BI套装软件服务,轻易便可搭建出一套Olap分析系统。不过BI的问题也随着时间的推移逐渐显露出来:

  • BI系统更多的以分析业务数据产生的密度高、价值高的结构化数据为主,对于非结构化和半结构化数据的处理非常乏力,例如图片,文本,音频的存储,分析。
  • 由于数据仓库为结构化存储,在数据从其他系统进入数据仓库这个东西,我们通常叫做ETL过程,ETL动作和业务进行了强绑定,通常需要一个专门的ETL团队去和业务做衔接,决定如何进行数据的清洗和转换。
  • 随着异构数据源的增加,例如如果存在视频,文本,图片等数据源,要解析数据内容进入数据仓库,则需要非常复杂等ETL程序,从而导致ETL变得过于庞大和臃肿。
  • 当数据量过大的时候,性能会成为瓶颈,在TB/PB级别的数据量上表现出明显的吃力。
  • 数据库的范式等约束规则,着力于解决数据冗余的问题,是为了保障数据的一致性,但是对于数据仓库来说,我们并不需要对数据做修改和一致性的保障,原则上来说数据仓库的原始数据都是只读的,所以这些约束反而会成为影响性能的因素。
  • ETL动作对数据的预先假设和处理,导致机器学习部分获取到的数据为假设后的数据,因此效果不理想。例如如果需要使用数据仓库进行异常数据的挖掘,则在数据入库经过ETL的时候就需要明确定义需要提取的特征数据,否则无法结构化入库,然而大多数情况是需要基于异构数据才能提取出特征。

在一系列的问题下,以Hadoop体系为首的大数据分析平台逐渐表现出优异性,围绕Hadoop体系的生态圈也不断的变大,对于Hadoop系统来说,从根本上解决了传统数据仓库的瓶颈的问题,但是也带来一系列的问题:

  1. 从数据仓库升级到大数据架构,是不具备平滑演进的,基本等于推翻重做。
  2. 大数据下的分布式存储强调数据的只读性质,所以类似于Hive,HDFS这些存储方式都不支持update,HDFS的write操作也不支持并行,这些特性导致其具有一定的局限性。

基于大数据架构的数据分析平台侧重于从以下几个维度去解决传统数据仓库做数据分析面临的瓶颈:

  1. 分布式计算:分布式计算的思路是让多个节点并行计算,并且强调数据本地性,尽可能的减少数据的传输,例如Spark通过RDD的形式来表现数据的计算逻辑,可以在RDD上做一系列的优化,来减少数据的传输。
  2. 分布式存储:所谓的分布式存储,指的是将一个大文件拆成N份,每一份独立的放到一台机器上,这里就涉及到文件的副本,分片,以及管理等操作,分布式存储主要优化的动作都在这一块。
  3. 检索和存储的结合:在早期的大数据组件中,存储和计算相对比较单一,但是目前更多的方向是在存储上做更多的手脚,让查询和计算更加高效,对于计算来说高效不外乎就是查找数据快,读取数据快,所以目前的存储不单单的存储数据内容,同时会添加很多元信息,例如索引信息。像类似于parquet和carbondata都是这样的思想。

总的来说,目前围绕Hadoop体系的大数据架构大概有以下几种:

传统大数据架构

​之所以叫传统大数据架构,是因为其定位是为了解决传统BI的问题,简单来说,数据分析的业务没有发生任何变化,但是因为数据量、性能等问题导致系统无法正常使用,需要进行升级改造,那么此类架构便是为了解决这个问题。可以看到,其依然保留了ETL的动作,将数据经过ETL动作进入数据存储。

优点:简单,易懂,对于BI系统来说,基本思想没有发生变化,变化的仅仅是技术选型,用大数据架构替换掉BI的组件。

缺点:对于大数据来说,没有BI下如此完备的Cube架构,虽然目前有kylin,但是kylin的局限性非常明显,远远没有BI下的Cube的灵活度和稳定度,因此对业务支撑的灵活度不够,所以对于存在大量报表,或者复杂的钻取的场景,需要太多的手工定制化,同时该架构依旧以批处理为主,缺乏实时的支撑。

适用场景:数据分析需求依旧以BI场景为主,但是因为数据量、性能等问题无法满足日常使用。

流式架构

在传统大数据架构的基础上,流式架构非常激进,直接拔掉了批处理,数据全程以流的形式处理,所以在数据接入端没有了ETL,转而替换为数据通道。经过流处理加工后的数据,以消息的形式直接推送给了消费者。虽然有一个存储部分,但是该存储更多的以窗口的形式进行存储,所以该存储并非发生在数据湖,而是在外围系统。

优点:没有臃肿的ETL过程,数据的实效性非常高。

缺点:对于流式架构来说,不存在批处理,因此对于数据的重播和历史统计无法很好的支撑。对于离线分析仅仅支撑窗口之内的分析。

适用场景:预警,监控,对数据有有效期要求的情况。

Lambda架构

Lambda架构算是大数据系统里面举足轻重的架构,大多数架构基本都是Lambda架构或者基于其变种的架构。Lambda的数据通道分为两条分支:实时流和离线。实时流依照流式架构,保障了其实时性,而离线则以批处理方式为主,保障了最终一致性。什么意思呢?流式通道处理为保障实效性更多的以增量计算为主辅助参考,而批处理层则对数据进行全量运算,保障其最终的一致性,因此Lambda最外层有一个实时层和离线层合并的动作,此动作是Lambda里非常重要的一个动作,大概的合并思路如下:

优点:既有实时又有离线,对于数据分析场景涵盖的非常到位。

缺点:离线层和实时流虽然面临的场景不相同,但是其内部处理的逻辑却是相同,因此有大量冗余和重复的模块存在。

适用场景:同时存在实时和离线需求的情况。

Kappa架构

​ Kappa架构在Lambda 的基础上进行了优化,将实时和流部分进行了合并,将数据通道以消息队列进行替代。因此对于Kappa架构来说,依旧以流处理为主,但是数据却在数据湖层面进行了存储,当需要进行离线分析或者再次计算的时候,则将数据湖的数据再次经过消息队列重播一次则可。

优点:Kappa架构解决了Lambda架构里面的冗余部分,以数据可重播的超凡脱俗的思想进行了设计,整个架构非常简洁。

缺点:虽然Kappa架构看起来简洁,但是施难度相对较高,尤其是对于数据重播部分。

适用场景:和Lambda类似,改架构是针对Lambda的优化。

Unified架构

​ 以上的种种架构都围绕海量数据处理为主,Unifield架构则更激进,将机器学习和数据处理揉为一体,从核心上来说,Unifield依旧以Lambda为主,不过对其进行了改造,在流处理层新增了机器学习层。可以看到数据在经过数据通道进入数据湖后,新增了模型训练部分,并且将其在流式层进行使用。同时流式层不单使用模型,也包含着对模型的持续训练。

优点:Unifield架构提供了一套数据分析和机器学习结合的架构方案,非常好的解决了机器学习如何与数据平台进行结合的问题。

缺点:Unifield架构实施复杂度更高,对于机器学习架构来说,从软件包到硬件部署都和数据分析平台有着非常大的差别,因此在实施过程中的难度系数更高。

适用场景:有着大量数据需要分析,同时对机器学习方便又有着非常大的需求或者有规划。

总结

以上几种架构为目前数据处理领域使用比较多的几种架构,当然还有非常多其他架构,不过其思想都会或多或少的类似。数据领域和机器学习领域会持续发展,以上几种思想或许终究也会变得过时。


更多精彩洞见,请关注微信公众号:思特沃克

Share

CSS-in-JS,向Web组件化再迈一大步

简介

CSS-in-JS是什么,看到这个词就能大概猜到是在JavaScript里写CSS,那为什么要在JavaScript里写CSS呢,像之前一样写在css文件里哪里不好么?

在介绍这个概念之前,先来回顾一下在日常编写CSS代码时都有哪些痛点:

  • 全局污染 – CSS的选择器是全局生效的,所以在class名称比较简单时,容易引起全局选择器冲突,导致样式互相影响。
  • 命名混乱 – 因为怕全局污染,所以日常起class名称时会尽量加长,这样不容易重复,但当项目由多人维护时,很容易导致命名风格不统一。
  • 样式重用困难 – 有时虽然知道项目上已有一些相似的样式,但因为怕互相影响,不敢重用。
  • 代码冗余 – 由于样式重用的困难性等问题,导致代码冗余。

进化史介绍

在CSS的进化历史上,出现过各种各样的框架致力于解决以上的问题:

  • SASS, LESS – 提供了变量、简单函数、运算、继承等,扩展性、重用性都有了很大的提升,解决了一些样式重用冗余的问题,但是对于命名混乱问题的效果不大。
  • BEM (.block__element–modifier) – 比较流行的class命名规则,部分解决了命名混乱和全局污染的问题,但class定义起来还是不太方便,比较冗长,而且和第三方库的命名还是有可能冲突。
  • CSS Modules – 模块化CSS,将CSS文件以模块的形式引入到JavaScript里,基本上解决了全局污染、命名混乱、样式重用和冗余的问题,但CSS有嵌套结构的限制(只能一层),也无法方便的在CSS和JavaScript之间共享变量。

可以看一个简单的CSS Modules例子了解一下:

生成的dom结构如下图,基于css文件中的class名称生成了唯一的class名称,样式会定义到生成的class上。

styles打印出来如下图,定义了css中的class名字和生成的唯一class名字的对应关系。

可以看出,以上框架都解决了不少痛点,但也还是各有一些不足,当然CSS-in-JS也并不是完美的解决了所有问题,我们先来详细介绍一下。

流行框架介绍

现在随着组件化概念的流行,对从组件层面维护CSS样式的需求日益增大,CSS-in-JS就是在组件内部使用JavaScript对CSS进行了抽象,可以对其声明和加以维护。这样不仅降低了编写CSS样式带来的风险,也让开发变得更加轻松。它和CSS Modules的区别是不再需要CSS样式文件。

来看一下几个流行的CSS-in-JS框架六个月内的下载趋势

我们来看看几个下载量靠前的框架的风格是什么样的:

styled-components

先来看看下载量最高的styled-component的代码风格:

从上图可以看出,Title和Wrapper都是框架包装好的component,可以直接在react的jsx语法中使用,在包装component的时候还定义了标签分别是h1和section。此段代码产生的html dom如下图所示:

可以看到section和h1上分别生成了唯一的class名称,样式也对应的定义在生成的class上了。

这样就可以解决命名混乱和全局污染的问题。组件相关的代码都在一起,可以统一查看,也可以方便的重用样式。

glamorous

再来看看glamorous,这个框架是PayPal开发的。(前两个logo看下来,恍惚间感觉进了化妆品专柜)。

和styled-component不同的是,glamorous的样式直接以attribute的形式定义在了dom上,之后虽然也为其生成了class名称及样式,但这种以attribute定义的方式对伪类选择符(如 :hover)支持的不好,会带来一些不方便,而且需要再记住一套attributes名称和值与真正的css样式代码的对应关系。

JSS

和上面两个框架类似,jss也是会定义styles对象,并附到component上,最后生成的dom也是会有生成的唯一class名称,并有对应的样式,但样式并不是真正的css语法,而是对象的属性和值,这样也是对伪类选择符支持的不好,而且也需要记住属性和css样式代码之间的对应关系。

Radium

Radium在定义样式对象上看似和其他相似,但在生成dom结构的时候并没有生成唯一的class名称,而是直接把样式放到了style属性上,这样会带来诸如可读性差、CSS权重过大、不支持伪类选择符等问题。

测试

下面再来看一个styled-component提供的基于jest的测试框架:

jest-styled-components

这个框架主要是通过生成Snapshot并比较的方式来保证component样式的每次更改都会被检测到,并且样式是期望的样式。这样就又降低了重构CSS样式带来的风险。

优劣势总结

看了这些框架后,可以发现CSS-in-JS的优势还是挺多的:

  • 因为有了生成的唯一class名称,避免了全局污染的问题
  • 唯一的class名称也解决了命名规则混乱的问题
  • JavaScript和CSS之间可以变量共享,比如一些基础的颜色和尺寸,这样再当需要在JavaScript里计算一些高度的时候,可以取到和dom相关的一些padding,margin数值,统一管理
  • 只生成页面需要用到的代码,缩减了最终包的大小,提升了性能
  • CSS的单元测试增加了样式重构的安全性

但是CSS-in-JS也存在着一些不足和争议:

  • 有些观点觉得JS和CSS的关系没这么近,把CSS写进JS里引入了新的一套依赖,增加了复杂度,新人加入项目后需要学习的东西就更多了,也让学习曲线更加陡了
  • 对前端框架确实有些依赖性,更适合于组件化的框架,如React等
  • Debug的时候需要花更多的功夫才能找到对应的样式代码
  • 覆盖第三方插件样式时会有权重不够的问题
  • Lint工具对于JavaScript内部的CSS代码样式支持的还不够

最后

在ThoughtWorks最新一期的技术雷达(CSS-in-JS | Technology Radar | ThoughtWorks)里,它的等级是Assess,表示的是:“值得追求。重要的是理解如何建立这种能力。企业应该在风险可控的项目中尝试此技术。” 所以最后想说的是,虽然它还是有些不足和争议,在应用之前需要多角度衡量一下对项目的适合度。但它的优点也很多,确确实实解决了很多痛点,而且与web组件化的方向高度一致,希望大家在条件合适的情况下多多尝试,多多反馈,这样也能促进整个CSS编码体验的继续进化~


更多精彩洞见,请关注微信公众号:思特沃克

Share

浅谈敏捷离岸团队沟通

很多人说,要让团队敏捷, 先让团队坐在一起。没错,“坐在一起”这区区几个字,可以解决团队从沟通到信任到效率提升的不少问题。作为团队的业务分析师,我们很多时候都扮演着产品端和开发端的黏合剂,最理想的工作环境可能是坐在产品团队和交付团队中间;办公室就是大舞台,随时都能展现自己的十八般武艺,把“坐在一起”的效应发挥到极致。

然而,由于种种原因,我们不免会遇到跨文化、跨时区的离岸团队。当“坐在一起”的舒适圈被打破后,很多问题则接踵而来:“见都没见过的团队成员怎么建立信任?”、“人都找不到怎么高效沟通?”、“时区空间不一致怎么组织工作坊?”经历了一个离岸团队的从无到有,淌过了大大小小的坑,笔者将所见所得整理成本文,希望能对同样纠结于此的同行们有一点点裨益。

按需互访

“见都没见过的团队成员怎么建立信任?” 答案恐怕是“无法建立”。别怪我这回答太消极任性,对于远隔重洋甚至黑夜颠倒的离岸团队而言,留言和视频所带来的困惑和乏力往往多过友好度和安全感。尤其对于那些毫无离岸合作经验的客户来说,面对这份未知和“不可控”往往是自我保护甚至抗拒的。

合作双方的互访则是建立信任的捷径,通过这扇窗户,一方面可以了解双方的企业文化、工作习惯乃至个人的性格特点;另一方面能够利用面对面工作坊高效梳理规划,最大程度保证后续项目推进方向的正确性。在条件允许的情况下,在项目伊始就建立互访机制:

  1. 在项目的重要节点,例如合作初始、项目里程碑、检查点进行在岸交流;
  2. 设立双向固定互访周期,如每三个月/半年。

当有机会去到客户现场,该如何充分利用短短数周或是数日?从个人经验来看,你或许可以:

  1. 做好充分的行前计划:不要把它当成签证材料准备中的“例行公事”。海外出差费时费力,为了确保效果,必须提前沟通准备并了解项目干系人的工作/休假安排,重要会议提前发好会议邀请,最好以邮件形式确认你的计划。相信我,你一定不想像我一样,人都到了客户现场以为一切自有安排,才发现接头人马上要休长假,独留“一脸懵逼”的自己。
  2. 尽量结识多的人:哪怕你自认不是“交际花”,哪怕自己的英语不够好,哪怕ta算不上直接干系人甚至来自不相干的团队/部门;当你们未来有工作上的交集,有一面之缘也大大好过冰冷的邮件。
  3. 定期和离岸团队沟通:即使有完整的记录,出差回来一次性输出的效果恐怕也差强人意。就算再忙再紧张,也要定期与团队交流,沟通自己在客户现场的收获,了解团队的问题和想法,并及时反馈到客户现场的工作计划中。

当客户即将来访,该如何抓住深化合作的机会?

  1. 最重要的依然是计划:提前为客户来访的工作内容做好细致的安排准备,双方设置好来访预期和目标,最大程度利用好团队与客户成员共同工作的时间。
  2. 提升对客户的影响力:帮客户站得更高,看得更广。尽力帮助他们在来访过程中更好地感受我们的工作方式和文化氛围,甚至以我们为窗口,介绍目前国内市场上的先进案例和最佳实践。
  3. 为客户做好行前准备和安排:这包括签证准备、交通酒店及个人饮食过敏源等在内的各类生活细碎。很多客户并没有到访中国的经验,对这个陌生又熟悉的国家甚至存在很多误会和担心。我们可以简单准备一份包含城市介绍、物价水平等信息的行前小材料,让他们不至于在踏上飞机前对即将来访的城市一无所知。

搭建沟通框架

对于“坐在一起”的敏捷团队,沟通会在工作和相处中自然而然地发生。而当我们所处的是一个离岸团队,很多沟通问题则会因为物理位置、语言文化障碍、时差而被放大;最危险的可能是,你不知道你不知道;而某些沟通隔阂可能会在某些时刻产生致命的影响。在项目初始就定义好沟通的渠道和方式在这样的环境下显得尤为重要。简单来讲,我们最应该关注的是:谁和谁沟通,通过什么形式沟通,达成怎样的目标,要有怎样的沟通频次。

(在项目初始与客户达成对重要会议的一致理解)

(对团队成员之间的沟通渠道需要统一和确认)

巧用工具

“时区空间不一致怎么组织工作坊?”针对这个问题,恐怕不仅仅是需求分析相关的工作坊,离岸团队的种种限制对我们许多熟悉的组织技巧和习以为常的敏捷实践都提出了挑战。互访是类似昂贵的特效药,而真正要身体好,还离不开悉心的日常调养。合适的工具,则是这里的关键。

1.Always On:如果能有超过四个小时的工作时间重叠,Always on一定要有。实时视频能增加许多亲切感和趣味,拉近团队成员的距离;更能让包括站会、desk check在内的很多敏捷实践变得容易。有了Always On,信息不回抬头喊一喊,开会了朝屏幕招招手,方便直接,也算对得起客户给的“魔镜”名号。

2.远程协作编辑软件:市面上的远程协作软件让人眼花缭乱,在这里分享几个你也许正在寻找的:

  • Keynote-Collaborate Mode: Keynote算得上我们目前使用最为频繁的演示软件,而它的Collaborate Mode这项高能技巧好像并不是那么知名。协同编辑除了能在紧张的项目节奏中提高团队效率,还可以帮助在重要演示环节/工作坊中与客户进行快速确认。举个例子,如果有两人共同参加工作坊,一人作为组织者与客户交流,另一位则可以实时将产出记录到Keynote中,在工作坊结束前第一时间呈现给客户进行确认。

(只需点击标题右下方的Collaborate,输入你要协作编辑对象的iCloud邮箱,即可将当前文件分享给对方进行协同编辑)

  • RealtimeBoard: 如果让我列举“2017年度最好用工具”,RealtimeBoard一定榜上有名:它是我目前能找到的最好的sticker board,是组织远距离工作坊的最佳搭档。除了最常用的反馈会议、头脑风暴外,RealtimeBoard提供了许多针对不同场景的实用模板,例如用户故事地图, 产品演进路线。

(RealtimeBoard: 五花八门的实用模板)

(团队的第一次远程回顾会议)

3.即时通讯工具:每个人每天都要用到,自然必不可少。 在与海外客户的合作中,常见的主要有Skype, Lync(Skype for business), Slack,HipChat, Hangouts等。目前我们项目正在用的是Hipchat,比较突出的亮点是不用翻墙、可以与Jira, Github集成,缺点主要是记录保存时间较短。

4.项目资产管理工具:个人认为,离岸团队要比在岸团队更加注重文档,良好的文档整理能降低沟通成本,也让沟通有迹可循。关于用户故事/电子看板,常见的有Jira, Trello, Pivotal Tracker, Mingle;关于其他项目相关文档管理,一般使用Confluence, Google Drive, DropBox等。

文化互通

文化差异是每个海外合作团队所必须面对的。由于文化背景的不同,团队成员们有不同的语言体系、做事方式,继而对合作产生一些显而易见或者不知不觉的影响。它本身是一个中性词,甚至褒义词:两种截然不同的文化碰撞,让我们能够交到大洋彼岸的朋友,了解彼此的文化,是多么幸运的一件事。而它也有可能成为“问题”:因为缺乏了解,导致双方产生误解甚至不信任,影响健康的合作关系。

承认并沟通合作双方的文化差异永远不会太早,在项目开始前/初期就应该主动地向客户介绍我们的文化:这包括了大文化,即国与国之间的文化差异,例如中国人可能会相对内向,不说话并不代表没有关注讨论;也包括了小文化,即组织和团队的文化差异,例如我们会相对自管理,不存在传统的上下级观念。

我们总是在谈论良好的团队氛围对项目成功的重要性。良好的团队氛围永远不是从单纯的工作沟通中来的,而是必须来自于每一个活生生的人。当团队成员之间建立起个人关系,很多问题都可以迎刃而解。

(当遇到文化碰撞,团队里需要有人挺身而出)

以上寥寥几点来自笔者有限的项目实践,不免有所遗漏或有待商榷。但毫无疑问的是,离岸团队需要更用心和精巧地经营,而成功也往往离不开各个角色的配合与贡献。


更多精彩洞见,请关注微信公众号:思特沃克

Share

微服务下使用GraphQL构建BFF

微服务架构,这个在几年前还算比较前卫的技术在如今遍地开花。得益于开源社区的支持,我们可以轻松地利用 Spring Cloud 以及 Docker 容器化快速搭建一个微服务架构的原型。不管是成熟的互联网公司、创业公司还是个人开发者,对于微服务架构的接纳程度都相当高,微服务架构的广泛应用也自然促进了技术本身更好的发展以及更多的实践。本文将结合项目实践,剖析在微服务的背景下,如何通过前后端分离的方式开发移动应用。

对于微服务本身,我们可以参考 Martin Fowler 对 Microservice 的阐述。简单说来,微服务是一种架构风格。通过对特定业务领域的分析与建模,将复杂的应用分解成小而专一、耦合度低并且高度自治的一组服务。微服务中的每个服务都是很小的应用,这些应用服务相互独立并且可部署。微服务通过对复杂应用的拆分,达到简化应用的目的,而这些耦合度较低的服务则通过 API 形式进行通信,所以服务之间对外暴露的都是 API,不管是对资源的获取还是修改。

微服务架构的这种理念,和前后端分离的理念不谋而合,前端应用控制自己所有的 UI 层面的逻辑,而数据层面则通过对微服务系统的 API 调用完成。以 JSP (Java Server Pages) 为代表的前后端交互方式也逐渐退出历史舞台。前后端分离的迅速发展也得益于前端 Web 框架 (Angular, React 等) 的不断涌现,单页面应用(Single Page Application)迅速成为了一种前端开发标准范式。加之移动互联网的发展,不管是 Mobile Native 开发方式,还是 React Native / PhoneGap 之流代表的 Hybrid 应用开发方式,前后端分离让 Web 和移动应用成为了客户端。客户端只需要通过 API 进行资源的查询以及修改即可。

BFF 概况及演进

Backend for Frontends(以下简称BFF) 顾名思义,是为前端而存在的后端(服务)中间层。即传统的前后端分离应用中,前端应用直接调用后端服务,后端服务再根据相关的业务逻辑进行数据的增删查改等。那么引用了 BFF 之后,前端应用将直接和 BFF 通信,BFF 再和后端进行 API 通信,所以本质上来说,BFF 更像是一种“中间层”服务。下图看到没有BFF以及加入BFF的前后端项目上的主要区别。

1. 没有BFF 的前后端架构

在传统的前后端设计中,通常是 App 或者 Web 端直接访问后端服务,后台微服务之间相互调用,然后返回最终的结果给前端消费。对于客户端(特别是移动端)来说,过多的 HTTP 请求是很昂贵的,所以开发过程中,为了尽量减少请求的次数,前端一般会倾向于把有关联的数据通过一个 API 获取。在微服务模式下,意味着有时为了迎合客户端的需求,服务器常会做一些与UI有关的逻辑处理

2. 加入了BFF 的前后端架构

加入了BFF的前后端架构中,最大的区别就是前端(Mobile, Web) 不再直接访问后端微服务,而是通过 BFF 层进行访问。并且每种客户端都会有一个BFF服务。从微服务的角度来看,有了 BFF 之后,微服务之间的相互调用更少了。这是因为一些UI的逻辑在 BFF 层进行了处理。

BFF 和 API Gateway

从上文对 BFF 的了解来看,BFF 既然是前后端访问的中间层服务,那么 BFF 和 API Gateway 有什么区别呢?我们首先来看下 API Gateway 常见的实现方式。(API Gateway 的设计方式可能很多,这里只列举如下三种)

1. API Gateway 的第一种实现:一个 API Gateway 对所有客户端提供同一种 API

单个 API Gateway 实例,为多种客户端提供同一种API服务,这种情况下,API Gateway 不对客户端类型做区分。即所有 /api/users的处理都是一致的,API Gateway 不做任何的区分。如下图所示:

2. API Gateway 的第二种实现:一个 API Gateway 对每种客户端提供分别的 API

单个 API Gateway 实例,为多种客户端提供各自不同的API。比如对于 users 列表资源的访问,web 端和 App 端分别通过 /services/mobile/api/users, /services/web/api/users服务。API Gateway 根据不同的 API 判定来自于哪个客户端,然后分别进行处理,返回不同客户端所需的资源。

3. API Gateway 的第三种实现:多个 API Gateway 分别对每种客户端提供分别的 API

在这种实现下,针对每种类型的客户端,都会有一个单独的 API Gateway 响应其 API 请求。所以说 BFF 其实是 API Gateway 的其中一种实现模式。

GraphQL 与 REST

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.

GraphQL 作为一种 API 查询语句,于2015年被 Facebook 推出,主要是为了替代传统的 REST 模式,那么对于 GraphQL 和 REST 究竟有哪些异同点呢?我们可以通过下面的例子进行理解。

按照 REST 的设计标准来看,所有的访问都是基于对资源的访问(增删查改)。如果对系统中 users 资源的访问,REST 可能通过下面的方式访问:

Request:

GET http://localhost/api/users

Response:

[
  {
    "id": 1,
    "name": "abc",
    "avatar": "http://cdn.image.com/image_avatar1"
  },
  ...
]
  • 对于同样的请求如果用 GraphQL 来访问,过程如下:

Request:

POST http://localhost/graphql

Body:

query {users { id, name, avatar } }

Response:

{
  "data": {
    "users": [
      {
        "id": 1,
        "name": "abc",
        "avatar": "http://cdn.image.com/image_avatar1"
      },
      ...
    ]
  }
}

关于 GraphQL 更详细的用法,我们可以通过查看文档以及其他文章更加详细的去了解。相比于 REST 风格,GraphQL 具有如下特性:

1. 定义数据模型:按需获取

GraphQL 在服务器实现端,需要定义不同的数据模型。前端的所有访问,最终都是通过 GraphQL 后端定义的数据模型来进行映射和解析。并且这种基于模型的定义,能够做到按需索取。比如对上文 /users 资源的获取,如果客户端只关心 user.id, user.name 信息。那么在客户端调用的时候,query 中只需要传入 users {id \n name}即可。后台定义模型,客户端只需要获取自己关心的数据即可。

2. 数据分层

查询一组users数据,可能需要获取 user.friends, user.friends.addr 等信息,所以针对 users 的本次查询,实际上分别涉及到对 user, frind, addr 三类数据。GraphQL 对分层数据的查询,大大减少了客户端请求次数。因为在 REST 模式下,可能意味着每次获取 user 数据之后,需要再次发送 API 去请求 friends 接口。而 GraphQL 通过数据分层,能够让客户端通过一个 API获取所有需要的数据。这也就是 GraphQL(图查询语句 Graph Query Language)名称的由来。

{
  user(id:1001) { // 第一层
    name,
    friends { // 第二层
      name,
      addr { // 第三层
        country,
        city
      }
    }
  }
}

3. 强类型

const Meeting = new GraphQLObjectType({
  name: 'Meeting',
  fields: () => ({
    meetingId: {type: new GraphQLNonNull(GraphQLString)},
    meetingStatus: {type: new GraphQLNonNull(GraphQLString), defaultValue: ''}
  })
})

GraphQL 的类型系统定义了包括 Int, Float, String, Boolean, ID, Object, List, Non-Null 等数据类型。所以在开发过程中,利用强大的强类型检查,能够大大节省开发的时间,同时也很方便前后端进行调试。

4. 协议而非存储

GraphQL 本身并不直接提供后端存储的能力,它不绑定任何的数据库或者存储引擎。它利用已有的代码和技术进行数据源的管理。比如作为在 BFF 层使用 GraphQL, 这一层的 BFF 并不需要任何的数据库或者存储媒介。GraphQL 只是解析客户端请求,知道客户端的“意图”之后,再通过对微服务API的访问获取到数据,对数据进行一系列的组装或者过滤。

5. 无须版本化

const PhotoType = new GraphQLObjectType({
  name: 'Photo',
  fields: () => ({
    photoId: {type: new GraphQLNonNull(GraphQLID)},
    file: {
      type: new GraphQLNonNull(FileType),
      deprecationReason: 'FileModel should be removed after offline app code merged.',
      resolve: (parent) => {
        return parent.file
      }
    },
    fileId: {type: new GraphQLNonNull(GraphQLID)}
  })
})

GraphQL 服务端能够通过添加 deprecationReason,自动将某个字段标注为弃用状态。并且基于 GraphQL 高度的可扩展性,如果不需要某个数据,那么只需要使用新的字段或者结构即可,老的弃用字段给老的客户端提供服务,所有新的客户端使用新的字段获取相关信息。并且考虑到所有的 graphql 请求,都是按照 POST /graphql 发送请求,所以在 GraphQL 中是无须进行版本化的。

GraphQL 和 REST

对于 GraphQL 和 REST 之间的对比,主要有如下不同:

1. 数据获取:REST 缺乏可扩展性, GraphQL 能够按需获取。GraphQL API 调用时,payload 是可以扩展的;

2. API 调用:REST 针对每种资源的操作都是一个 endpoint, GraphQL 只需要一个 endpoint( /graphql), 只是 post body 不一样;

3. 复杂数据请求:REST 对于嵌套的复杂数据需要多次调用,GraphQL 一次调用, 减少网络开销;

4. 错误码处理:REST 能够精确返回HTTP错误码,GraphQL 统一返回200,对错误信息进行包装;

5. 版本号:REST通过 v1/v2 实现,GraphQL 通过 Schema 扩展实现;

微服务 + GraphQL + BFF 实践

在微服务下基于 GraphQL 构建 BFF,我们在项目中已经开始了相关的实践。在我们项目对应的业务场景下,微服务后台有近 10 个微服务,客户端包括针对不同角色的4个 App 以及一个 Web 端。对于每种类型的 App,都有一个 BFF 与之对应。每种 BFF 只服务于这个 App。BFF 解析到客户端请求之后,会通过 BFF 端的服务发现,去对应的微服务后台通过 CQRS 的方式进行数据查询或修改。

1. BFF 端技术栈

我们使用 GraphQL-express 框架构建项目的 BFF 端,然后通过 Docker 进行部署。BFF 和微服务后台之间,还是通过 registrator 和 Consul 进行服务注册和发现。

  addRoutes () {
    this.express.use('/graphql', this.resolveFromRequestScopeAndHandle('GraphqlHandler'))
    this.serviceNames.forEach(serviceName => {
      this.express.use(`/api/${serviceName}`, this.routers.apiProxy.createRouter(serviceName))
    })
  }

在 BFF 的路由设置中,对于客户端的处理,主要有 /graphql/api/${serviceName}两部分。/graphql 处理的是所有 GraphQL 查询请求,同时我们在 BFF 端增加了 /api/${serviceName} 进行 API 透传,对于一些没有必要进行 GraphQL 封装的请求,可以直接通过透传访问到相关的微服务中。

2. 整体技术架构

整体来看,我们的前后端架构图如下,三个 App 客户端分别使用 GraphQL 的形式请求对应的 BFF。BFF 层再通过 Consul 服务发现和后端通信。

关于系统中的鉴权问题

用户登录后,App 直接访问 KeyCloak 服务获取到 id_token,然后通过 id_token 透传访问 auth-api 服务获取到 access_token, access_token 以 JWT (Json Web Token) 的形式放置到后续 http 请求的头信息中。

在我们这个系统中 BFF 层并不做鉴权服务,所有的鉴权过程全部由各自的微服务模块负责。BFF 只提供中转的功能。BFF 是否需要集成鉴权认证,主要看各系统自己的设计,并不是一个标准的实践。

3. GraphQL + BFF 实践

通过如下几个方面,可以思考基于 GraphQL 的 BFF 的一些更好的特质:

GraphQL 和 BFF 对业务点的关注

从业务上来看,PM App(使用者:物业经理)关注的是property,物业经理管理着一批房屋,所以需要知道所有房屋概况,对于每个房屋需要知道有没有对应的维修申请。所以 PM App BFF 在定义数据结构是,maintemamceRequestsproperty 的子属性。

同样类似的数据,Supplier App(使用者:房屋维修供应商)关注的是 maintenanceRequest(维修工单),所以在 Supplier App 获取的数据里,我们的主体是maintenanceRequest。维修供应商关注的是 workOrder.maintenanceRequest

所以不同的客户端,因为存在着不同的使用场景,所以对于同样的数据却有着不同的关注点。BFF is pary of Application。从这个角度来看,BFF 中定义的数据结构,就是客户端所真正关心的。BFF 就是为客户端而生,是客户端的一部分。需要说明的是,对于“业务的关注”并不是说,BFF会处理所有的业务逻辑,业务逻辑还是应该由微服务关心,BFF 关注的是客户端需要什么。

GraphQL 对版本化的支持

假设 BFF 端已经发布到生产环境,提供了 inspection 相关的 tenantslandlords 的查询。现在需要将图一的结构变更为图二的结构,但是为了不影响老用户的 API 访问,这时候我们的 BFF API 必须进行兼容。如果在 REST 中,可能会增加 api/v2/inspections进行 API 升级。但是在 BFF 中,为了向前兼容,我们可以使用图三的结构。这时候老的 APP 使用黄色区域的数据结构,而新的 APP 则使用蓝色区域定义的结构。

GraphQL Mutation 与 CQRS

mutation {
  area {
    create (input: {
      areaId:"111", 
      name:"test", 
    })
  }
}

如果你详细阅读了 GraphQL 的文档,可以发现 GraphQL 对 querymutation 进行了分离。所有的查询应该使用 query { ...},相应的 mutaition 需要使用 mutation { ... }。虽然看起来像是一个convention,但是 GraphQL 的这种设计和后端 API 的 读写职责分离(Command Query Responsibility Segregation)不谋而合。而实际上我们使用的时候也遵从这个规范。所以的 mutation 都会调用后台的 API,而后端的 API 对于资源的修改也是通过 SpringBoot EventListener 实现的 CQRS 模式。

如何做好测试

在引入了 BFF 的项目,我们的测试仍然使用金字塔原理,只是在客户端和后台之间,需要添加对 BFF 的测试。

  • Client 的 integration-test 关心的是 App 访问 BFF 的连通性,App 中所有访问 BFF 的请求都需要进行测试;
  • BFF 的 integration-test 测试的是 BFF 到微服务 API 的连通性,BFF 中依赖的所有 API 都应该有集成测试的保障;
  • API 的 integration-test 关注的是这个服务对外暴露的所有 API,通常测试所有的 Controller 中的 API;

结语

微服务下基于 GraphQL 构建 BFF 并不是银弹,也并不一定适合所有的项目,比如当你使用 GraphQL 之后,你可能得面临多次查询性能问题等,但这不妨碍它成为一个不错的尝试。你也的确看到 Facebook 早已经使用 GraphQL,而且 Github 也开放了 GraphQL 的API。而 BFF, 其实很多团队也都已经在实践了,在微服务下等特殊场景下,GraphQL + BFF 也许可以给你的项目带来惊喜。

参考资料

【注】部分图片来自网络


更多精彩洞见,请关注微信公众号:思特沃克

Share