吐槽文一篇。
在实践前后端分离的这些年来,已经诞生了一些技术与工具让前后端进行沟通:
而使用这些工具的时候,往往容易出现一些问题。
消费者驱动,顾名思义就是在我们协定契约的时候,按消费者的业务实现角度,与生产者一起协定契约。在实践上,往往由于后端在项目中的主导地位导致:协商的结果不会按消费者需要,而是按生者需要来生成契约。造成这种结果的因素有很多:
我的意思并不是说,按生产者来主导有什么问题,而是配不上『消费者驱动』这五个字。诸如于我们获取某个 API 的时候,前端所需要的是相关的信息,而不是对应的实体 ID。而后,再由前端去获取对应实体的信息。从逻辑上来说,由前端获取或者后端获取,从技术上来说,并没有多大的问题。但是从用户体验上来说,并没有那么友好。后端间的 API 调用,可以是几十 ms 级别的;而前端多调用一个后端 API,在网络上的传输时间,往往都是几百 ms,乃至几秒。
所以,让我们再看看各个团队所宣称的消费者驱动的契约测试。
消费者驱动的契约测试(Consumer-Driven Contracts,简称CDC),是指从消费者业务实现的角度出发,驱动出契约,再基于契约,对提供者验证的一种测试方式。
如果你不是消费者驱动的契约,而是后端一口气定出来的接口,你怎么能叫『消费者驱动的契约测试』????按这样的做法,它只能叫生产者的契约测试。
既然是生产者的契约测试,那么它就无法保证说:API 修改不影响消费者使用——因为消费者压根就没有参与到契约的制定,生产者可以按照自己的需要来修改契约。
所以,生产者到底是在给谁测试?又是在测试什么东西?
在大部分的项目中,对于契约的使用存在问题,即模式错了。契约测试需要围绕业务流程施展,而不应该相互隔离。如果契约的用例不丰富,便无法串起整个系统的流程。而作为 API 的提供方,应该保证业务逻辑是可以串联起来的。从用户的登录开始,到用户获取数据,展示列表页,能进入详情页等等。
我们做契约测试的目标是,让代码修改不影响 API 输出。后端需求一改,契约测试会挂,后端知道出现问题,会进行修复。如果前端用的是契约测试的契约提供的 Mock Server,前端的测试也会挂,这样问题就不会往后流。
所以,契约不应当给前端做本地开发和测试 。
对于小团队来说,去采用原始的 API 方式反而更加有效率;对于大团队来说,这样的方式并不可行。
既然,沟通那么麻烦,那么我们就分开吧。如果契约和 Mock Server 是分开的,那么维护前端 Mock Server 的人很很难识别到修改。相关的问题,在测试不及时的情况下,有可能在上线后才发现。
也因此,从理想情况来说,这个 Mock Server 是契约动态生成的,并能根据后端现有最新的契约更新。或者,后端在实现契约测试时,要做的是只测试部分数据,而不是为了测试而测试。
尽管 DDD 模型不一定非要与 API 绑定,但是由于种种原因,真正在实践的时候,就不是这么一回事。我们可以看到在很多的项目里,DDD 返回的实体资源,最后可能与 API 的字段一致的。但是它并非是业务上想要返回的逻辑,各个客户端(PC Web、小程序、Android 端、iOS 端等)还都需要进行二次的处理。
这就是为什么,DDD + 微服务,一定要配合一下 BFF 的原因。
实施 BFF,大家并没有啥问题。可一谁来维护这一层胶水层的时候,大家都问题都来了——前后端都不愿意维护这个 BFF。对于前端来说,开发者只是 API 的消费者,突然间又变身成为生产者;对于后端来说,开发者原本不需要了解显示逻辑,现在也要涉及到这部分的内容。
不过呢,我们可以保持一致的是:后端是 API 的生产者。而如果生产者没有参与到 BFF 的开发,那么就会引发另外一个问题,API 发生变更的时候,可能没有同步到 BFF——尽管每个项目都有自己的规范,但是只要是由人来执行的,都可能会出现问题。
那么,我们就需要一个 BFF 的契约测试,那在第一时间知道 API 的变化。虽然感觉怪怪的,但是我们还是将客户端的契约测试,前移至了 BFF 层。
所以,消费者驱动的契约在哪里呢?
大家都懂了。
前端写 BFF 并不是一件容易的事。你要懂微前端,要懂主流的微服务框架,如 Spring Cloud,要懂 Java。与此同时,还需要了解各个 API 的变化 情况。对于前后端分离团队而言,要做样的工作太难了。
而尽管你可以采用 Node.js + TypeScript,但是它也意味着你要成为一个 Node.js 专家。如果让我用 Node.js 写 BFF,那我还是宁愿和以前的项目一样,写 Scala 来作为 BFF。
所以,我并不推荐使用 Node.js 作为 BFF。一来,没有阿里巴巴强大的 Node.js 专家群;二来,我对于成为 Node.js 没有兴趣。用擅长于某一领域的语言去做某一领域的事,而非用擅长的语言去做每一领域的事——兴趣和爱好除外。
大家都懂了。
参考《整洁前端架构》。
我们在诸多项目实现了:针对于生产者的契约测试。其中也包含了之前我写的 mest
框架,它是通过结合 TypeScript 的 Interface 来验证后端接口是否一致。
当你没有办法的时候,这也是一个不那么差的做法。换个问题来思考的话,就是以生产者来解决这些问题。
直接转换模型为 Java Class 和 TypeScript 是一种更简单的做法。
没有银弹。
围观我的Github Idea墙, 也许,你会遇到心仪的项目