Blog

Blog

PHODAL

消费者驱动契约已死?

吐槽文一篇。

契约的一些问题

在实践前后端分离的这些年来,已经诞生了一些技术与工具让前后端进行沟通:

  • 契约的 Mock 服务(Mock Server)。用于模拟一个服务器,为特定的接口返回特定的值。
  • 契约测试。对前后端协定的 API 进行测试。
  • 前后端胶水层。如 BFF (Backends for Frontends),根据客户端的需要对 API 进行聚合、适配、裁剪等。

而使用这些工具的时候,往往容易出现一些问题。

消费者无法驱动的契约

消费者驱动,顾名思义就是在我们协定契约的时候,按消费者的业务实现角度,与生产者一起协定契约。在实践上,往往由于后端在项目中的主导地位导致:协商的结果不会按消费者需要,而是按生者需要来生成契约。造成这种结果的因素有很多:

  1. 后端才是实现方。所以,你常会听见:『你行,你上』,笑~。
  2. 从实施成本考虑。诸如于跨服务调用的成本等等
  3. 后端的职级高——毕竟后端已经有了几十年历史了。
  4. 为了更『美』的后端架构——导致了更『差的』前端架构。

我的意思并不是说,按生产者来主导有什么问题,而是配不上『消费者驱动』这五个字。诸如于我们获取某个 API 的时候,前端所需要的是相关的信息,而不是对应的实体 ID。而后,再由前端去获取对应实体的信息。从逻辑上来说,由前端获取或者后端获取,从技术上来说,并没有多大的问题。但是从用户体验上来说,并没有那么友好。后端间的 API 调用,可以是几十 ms 级别的;而前端多调用一个后端 API,在网络上的传输时间,往往都是几百 ms,乃至几秒。

生产者的契约测试

所以,让我们再看看各个团队所宣称的消费者驱动的契约测试

消费者驱动的契约测试(Consumer-Driven Contracts,简称CDC),是指从消费者业务实现的角度出发,驱动出契约,再基于契约,对提供者验证的一种测试方式。

如果你不是消费者驱动的契约,而是后端一口气定出来的接口,你怎么能叫『消费者驱动的契约测试』????按这样的做法,它只能叫生产者的契约测试。

既然是生产者的契约测试,那么它就无法保证说:API 修改不影响消费者使用——因为消费者压根就没有参与到契约的制定,生产者可以按照自己的需要来修改契约。

所以,生产者到底是在给谁测试?又是在测试什么东西?

契约测试的契约作为 Mock Server——不行

在大部分的项目中,对于契约的使用存在问题,即模式错了。契约测试需要围绕业务流程施展,而不应该相互隔离。如果契约的用例不丰富,便无法串起整个系统的流程。而作为 API 的提供方,应该保证业务逻辑是可以串联起来的。从用户的登录开始,到用户获取数据,展示列表页,能进入详情页等等。

我们做契约测试的目标是,让代码修改不影响 API 输出。后端需求一改,契约测试会挂,后端知道出现问题,会进行修复。如果前端用的是契约测试的契约提供的 Mock Server,前端的测试也会挂,这样问题就不会往后流。

所以,契约不应当给前端做本地开发和测试 。

没有契约的 Mock Server——不行

对于小团队来说,去采用原始的 API 方式反而更加有效率;对于大团队来说,这样的方式并不可行。

既然,沟通那么麻烦,那么我们就分开吧。如果契约和 Mock Server 是分开的,那么维护前端 Mock Server 的人很很难识别到修改。相关的问题,在测试不及时的情况下,有可能在上线后才发现。

也因此,从理想情况来说,这个 Mock Server 是契约动态生成的,并能根据后端现有最新的契约更新。或者,后端在实现契约测试时,要做的是只测试部分数据,而不是为了测试而测试。

只有 DDD 的微服务——不行

尽管 DDD 模型不一定非要与 API 绑定,但是由于种种原因,真正在实践的时候,就不是这么一回事。我们可以看到在很多的项目里,DDD 返回的实体资源,最后可能与 API 的字段一致的。但是它并非是业务上想要返回的逻辑,各个客户端(PC Web、小程序、Android 端、iOS 端等)还都需要进行二次的处理。

这就是为什么,DDD + 微服务,一定要配合一下 BFF 的原因。

缺少前端、后端的 BFF——不行

实施 BFF,大家并没有啥问题。可一谁来维护这一层胶水层的时候,大家都问题都来了——前后端都不愿意维护这个 BFF。对于前端来说,开发者只是 API 的消费者,突然间又变身成为生产者;对于后端来说,开发者原本不需要了解显示逻辑,现在也要涉及到这部分的内容。

不过呢,我们可以保持一致的是:后端是 API 的生产者。而如果生产者没有参与到 BFF 的开发,那么就会引发另外一个问题,API 发生变更的时候,可能没有同步到 BFF——尽管每个项目都有自己的规范,但是只要是由人来执行的,都可能会出现问题。

那么,我们就需要一个 BFF 的契约测试,那在第一时间知道 API 的变化。虽然感觉怪怪的,但是我们还是将客户端的契约测试,前移至了 BFF 层。

所以,消费者驱动的契约在哪里呢?

解决方案

BFF

大家都懂了。

前端为主写 BFF

前端写 BFF 并不是一件容易的事。你要懂微前端,要懂主流的微服务框架,如 Spring Cloud,要懂 Java。与此同时,还需要了解各个 API 的变化 情况。对于前后端分离团队而言,要做样的工作太难了。

而尽管你可以采用 Node.js + TypeScript,但是它也意味着你要成为一个 Node.js 专家。如果让我用 Node.js 写 BFF,那我还是宁愿和以前的项目一样,写 Scala 来作为 BFF。

所以,我并不推荐使用 Node.js 作为 BFF。一来,没有阿里巴巴强大的 Node.js 专家群;二来,我对于成为 Node.js 没有兴趣。用擅长于某一领域的语言去做某一领域的事,而非用擅长的语言去做每一领域的事——兴趣和爱好除外。

GraphQL as BFF

大家都懂了。

前端防腐层

参考《整洁前端架构》。

消费者的契约测试

我们在诸多项目实现了:针对于生产者的契约测试。其中也包含了之前我写的 mest 框架,它是通过结合 TypeScript 的 Interface 来验证后端接口是否一致。

当你没有办法的时候,这也是一个不那么差的做法。换个问题来思考的话,就是以生产者来解决这些问题。

直接转换模型为 Java Class 和 TypeScript 是一种更简单的做法。

结论

没有银弹。


或许您还需要下面的文章:

关于我

Github: @phodal     微博:@phodal     知乎:@phodal    

微信公众号(Phodal)

围观我的Github Idea墙, 也许,你会遇到心仪的项目

QQ技术交流群: 321689806
comment

Feeds

RSS / Atom

最近文章

关于作者

Phodal Huang

Engineer, Consultant, Writer, Designer

ThoughtWorks 技术专家

工程师 / 咨询师 / 作家 / 设计学徒

开源深度爱好者

出版有《前端架构:从入门到微前端》、《自己动手设计物联网》、《全栈应用开发:精益实践》

联系我: h@phodal.com

微信公众号: 最新技术分享

标签