web后端开发-web后端开发学什么
面向 UI 和外部各方的单一用途边缘服务
介绍
随着 Web 的出现和成功,交付用户界面的实际方式已经从胖客户端应用程序转变为通过 Web 交付的界面,这一趋势也促进了基于 SAAS 的解决方案的开发。 通过 Web 提供用户界面的好处是巨大的——主要是因为发布新功能的成本大大降低,因为(在大多数情况下)完全消除了客户端安装成本。
然而,这个简单的世界并没有持续多久,没过多久,手机时代就到来了。 现在我们有麻烦了。 我们有服务器端功能,我们希望通过桌面 Web UI 和一个或多个移动 UI 公开这些功能。 鉴于系统最初是基于桌面 Web UI 开发的,我们在适应这些新型用户界面时经常遇到问题,因为桌面 Web UI 和我们支持的服务之间已经存在紧密耦合。
通用 API 后端
适应多种类型 UI 的第一步通常是提供单一的服务器端 API,随着时间的推移根据需要添加更多功能以支持新型移动交互:
通用 API 后端
如果这些不同的ui想要进行相同或者非常相似的调用,那么这样一个通用的API就很容易成功。 然而,移动体验的本质通常与桌面 web 体验截然不同。 首先,移动设备的价格差别很大。 我们拥有更少的屏幕空间,这意味着我们可以显示更少的数据。 打开与服务器端资源的大量连接会耗尽电池寿命并限制数据计划。 其次,我们希望在移动设备上提供的交互的性质可能会有很大差异。 想想一个典型的实体零售商。 在桌面应用程序中,我可能允许您查看打折商品、在线订购或在商店预订。 不过在移动设备上,我可能想让你扫描条形码来进行价格比较,或者在商店里为你提供基于上下文的报价。 随着我们构建越来越多的移动应用程序,我们意识到人们使用它们的方式非常不同,因此我们需要公开的功能也会有所不同。
因此,在实践中,我们的移动设备将进行不同的调用、更少的调用,并且将显示与桌面设备不同(可能更少)的数据。 这意味着我们需要向 API 后端添加额外的功能以支持我们的移动界面。
通用 API 后端的另一个问题是,根据定义,它们为多个面向用户的应用程序提供功能。 这意味着在推出新的交付时,单个 API 后端可能成为瓶颈,因为要对同一个可部署工件进行许多更改。
通用 API 后端倾向于承担多项责任,因此需要大量工作,这通常会导致创建专门负责该代码库的团队。 这使问题变得更糟,因为现在前端团队必须与一个单独的团队进行交互才能做出更改——一个团队必须平衡不同客户团队的优先级,并且还必须与多个下游团队合作,在在新 API 可用时使用它们。 可以这么说,此时我们刚刚在我们的架构中创建了一个智能中间件,它不专注于任何特定的业务领域,不像许多人认为一个明智的面向服务的架构应该是视图相互矛盾。
介绍前端后端
我在 REA 和 SoundCloud 中看到的针对此问题的一种解决方案是,不是拥有一个通用的 API 后端,而是每个用户都有一个后端,或者(前 SoundClouder)Phil Calçado 称之为前端后端终端 (BFF)。 从概念上讲,您应该将面向用户的应用程序视为两个组件:外围的客户端应用程序和外围的服务器端组件 (BFF)。
BFF 与特定的用户体验紧密耦合,通常由与 UI 相同的团队维护,从而更容易定义和调整 API 以满足 UI 的需求,同时还简化了客户端和服务器组件的排队发布过程.
每个 UI 使用服务器端 BFF
BFF 专注于一个 UI,并且只专注于那个 UI。 这使它能够聚焦并因此变得更小。
有多少BFF?
当谈到在不同平台上提供相同(或相似)的用户体验时,我看到了两种不同的方法。 我更喜欢的模型严格来说是为每种不同类型的客户分配一个 BFF——这是我在 REA 看到的一种模式:
不同的移动平台,不同的 BFF,对于 REA
我在 SoundCloud 上看到的另一种模型,每个 UI 使用一个 BFF。 因此,本机应用程序的 Android 和 iOS 版本都使用相同的 BFF:
有一个用于不同移动后端的 BFF,例如 SoundCloud 中使用的那个
我对第二种模式的主要担忧是,使用一个 BFF 的客户端类型越多,处理多个问题就越容易变得臃肿。 不过,这里要了解的关键是,即使 BFF 是共享的,它也针对相同类型的用户界面——因此,虽然 SoundCloud 的 iOS 和 Android 监听器原生应用程序使用相同的 BFF,但其他原生 Machine 应用程序将使用不同的 BFF(例如,新的 Creator 应用程序 Pulse 使用不同的 BFF)。 如果同一个团队同时拥有 Android 和 iOS 应用程序并且还拥有 BFF,我也可以更轻松地使用此模型 - 如果应用程序由不同团队维护,我更倾向于推荐更严格的模型。 因此,您可以将您的组织结构视为最有意义的模型的主要驱动力之一(康威定律再次获胜)。 值得注意的是,与我交谈过的 SoundCloud 工程师表示,如果今天再次做出决定,他们可能会重新考虑为 Android 和 iOS 监听器应用程序提供 BFF。
我非常喜欢 Stewart Gleadow(他反过来称赞了 Phil Calçado 和 Mustafa Sezgin)的指导方针之一是“一次经历,一个朋友”。 因此,如果 iOS 和 Android 体验非常相似,则更容易证明拥有 BFF 是合理的。 但是,如果它们非常不同,则拥有单独的 bff 更有意义。
皮特·霍奇森 (Pete Hodgson) 观察到,当围绕团队边界对齐时,bff 的效果最好,因此团队结构应该决定您有多少 bff。 所以如果你有一个移动团队,你应该有一个 BFF,但如果你有独立的 iOS 和 Android 团队,你将有单独的 BFF。 我担心的是团队结构往往比我们的系统设计更灵活。 那么如果你有一个移动BFF,然后把团队分成iOS和Android专业,你是不是也要把BFF分开? 如果 bff 已经是独立的,那么拆分团队会更容易,因为您可以重新分配已经独立的资产的所有权。 然而,BFF 和团队结构的相互作用很重要,我们将在后面探讨。
通常,实现少量 bff 的驱动程序是重用服务器端功能以避免过度重复,但还有其他方法可以解决这个问题,我们将在稍后介绍。
和多个下游服务(微服务!)
对于具有少量后端服务的架构,BFF 可能是一种有用的模式。 然而,它们对于使用大量服务的组织来说是必不可少的,因为聚合多个下游调用以提供用户功能的需求已经急剧增加。 在这种情况下,对 BFF 的一次调用通常会导致对微服务的多次下游调用。 例如,想象一个电子商务公司的应用程序。 我们想要获取用户愿望清单中的项目列表,显示库存水平和价格:
多项服务保存着我们想要的信息。 Wishlist 服务存储有关列表的信息,以及每个项目的 ID。 目录服务存储每个项目的名称和价格,库存水平存储在我们的库存服务中。 所以在我们的 BFF 中,我们将公开一个方法来检索完整的播放列表,它至少包含 3 个调用:
进行多次下游调用以构建心愿单视图
从效率的角度来看,并行运行尽可能多的调用会更明智。 一旦对 Wishlist 服务的初始调用完成,理想情况下我们希望同时运行对其他服务的调用以减少整体调用时间。 这种将我们希望并行运行的调用与顺序运行的调用混合在一起的需求很快就会变得难以管理,尤其是对于更复杂的场景。 当多个调用的组合变得更易于管理时,这是反应式编程风格(例如 RxJava 或 Finagle 的期货系统提供的)可以提供帮助的领域。
但是故障模式变得很重要。 在上面的示例中,我们可以坚持所有下游调用都必须返回,以便我们将有效负载返回给客户端。 然而,这是明智的吗? 显然,如果 Wishlist 服务宕机了,我们什么也做不了,但如果只有 Inventory 服务宕机,那么最好减少我们传递给客户端的功能,也许只是删除库存水平指示器? 首先,这些问题必须由 BFF 自己管理,但我们还需要确保调用 BFF 的客户端能够正确解释部分响应并进行渲染。
重用
每个 UI 都有一个 BFF 的一个问题是 BFF 本身之间可能存在大量重复。 例如,它们最终可能会执行相同类型的聚合,具有相同或相似的与下游服务交互的代码等。对此,一些人希望将这些代码合并在一起,以拥有一个通用的聚合边缘 API 服务。 这种模式一次又一次地证明,它会导致代码高度臃肿,将多个关注点塞在一起。
正如我之前多次说过的,我很乐意跨服务复制代码。 也就是说,虽然在单个进程边界内我通常会尽力将复制重构为适当的抽象,但在遇到跨服务复制时我不会有相同的反应。 这主要是因为我通常更担心引入共享代码导致服务之间紧密耦合的可能性——这比一般的复制更让我担心。 也就是说,在某些情况下,这是有道理的。
我的同事 Pete Hodgson 指出,如果你没有朋友,那么通常“共享”的逻辑最终会融入不同的客户端。 由于这些客户端使用非常不同的技术堆栈,因此很难确定是否发生了这种重复。 由于组织倾向于为服务器端组件构建通用技术堆栈,因此拥有多个重复的 BFF 可能更容易发现和排除。
当需要提取共享代码时,有两个明显的选择。 第一种是提取某种共享库,这通常是最便宜的,但更令人担忧。 这可能有问题的原因是共享库是耦合的主要来源,尤其是在用于生成客户端库以调用下游服务时。 尽管如此,在某些情况下这感觉是正确的——尤其是当被抽象的代码纯粹是服务内部的一个问题时。
另一种选择是在新服务中提取共享功能,如果您可以概念化新服务具有围绕相关域建模的某些东西,那么这种方法效果很好。
这种方法的一种变体可能是将聚合的责任推给更下游的服务。 以上面的例子为例,我们讨论了心愿单的渲染。 假设我们在两个地方渲染了一个心愿单——在 Android 上,在 iOS Web 上。 我们的每一位朋友都在打同样的三个电话:
多个 BFF 执行相同任务
相反,我们可以更改 Wishlist 服务来为我们进行下游调用,从而使调用者更容易:
将征收关税进一步推向下游,以消除 bff 中的重复
我不得不说web后端开发,在两个地方有相同的代码并不一定会导致我想以这种方式提取服务,但如果创建新服务的交易成本足够低,或者我有多个地方(例如,在桌面网络)使用它,我肯定会考虑它。 我认为即使在服务级别,当您要第三次实现某个东西时创建抽象仍然是一个很好的经验法则。
桌面网络及其他领域的好友
您可以认为 BFF 仅对解决移动设备的限制有用。 桌面网络体验通常在功能更强大、连接性更好的设备上提供,在这些设备上进行多个下游调用的成本是可控的。 这允许您的 Web 应用程序在没有 BFF 的情况下直接对下游服务进行多次调用。
我还看到在网络上使用 BFF 也很有用的案例。 当您在服务器端生成大部分 Web UI(例如,使用服务器端模板)时,BFF 是执行此操作的明显位置。 它还在某种程度上简化了缓存,因为您可以在 BFF 前面放置一个反向代理,允许您缓存聚合调用的结果(尽管您必须确保相应地设置缓存控制以确保聚合内容过期同时在最短的时间内聚合最新内容)。 事实上,我已经看到它被使用了很多次,但并没有称它为 BFF——事实上web后端开发,常见的 API 后端通常都是从这样的野兽中发展而来的。
我已经看到至少有一个组织将 bff 用于需要呼叫的其他外部各方。 回到我多年的音乐商店示例,我可能会公开一个 BFF,它允许第三方提取版税支付信息,提供 Facebook 集成或允许流式传输到一系列机顶设备:
使用 BFF 将 API 暴露给第三方
这种方法特别有效,因为第三方通常没有能力(或不想)使用或更改他们进行的 API 调用。 对于通用 API 后端,您可能必须保留旧版本的 API 以满足您无法更改的一小部分外部方的需求——使用 BFF,这个问题会大大减少。
和自治
我们经常看到一个团队在前端工作而另一个团队在创建后端服务的情况。 通常,我们试图通过围绕垂直业务转向微服务来避免这种情况,但即便如此,也存在不可避免的情况。 首先,在一定规模或复杂性下,需要多个团队。 其次,执行良好的 Android 或 iOS 体验所需的技术技能深度通常需要专门的团队。
因此,构建 UI 的团队面临这样一种情况,即他们正在调用另一个团队正在驱动的 API,并且通常在开发 UI 的同时 API 也在不断发展。 BFF 在这里可以提供帮助,尤其是当它属于创建用户界面的同一团队时。 他们在创建前端的同时改进了 BFF 的 API。 他们可以非常快速地重复这两个过程。 BFF 本身仍然需要调用其他下游服务,但这可以在不中断 UI 开发的情况下完成。
使用 bff 时的团队所有权边界示例
使用与团队边界保持一致的 BFF 的另一个好处是,创建界面的团队可以更灵活地考虑功能所在的位置。 例如,他们可以决定将功能推送到服务器端以促进未来的重用并简化原生移动应用程序,或者允许更快地发布新功能(因为您可以绕过应用程序商店审查流程)。 如果团队同时拥有移动应用程序和 BFF,则可以由团队单独做出此决定——这不需要任何跨团队协调。
一般外设问题
有些人将 bffs 用于一般外围设备问题,如身份验证/授权或请求日志记录。 我对此很恼火。 一方面,大部分功能都是通用的,所以我倾向于使用位于上游的另一层来实现它,也许使用 Nginx 或 Apache 服务器之类的东西。 另一方面,这样的额外层也会增加延迟。 BFF 通常用于微服务环境,在这些环境中,由于进行了大量网络调用,我们已经对延迟非常敏感。 此外,要构建类似生产的堆栈,需要部署的层越多,开发和测试就会变得越复杂——将所有这些问题放在 BFF 中作为一个更独立的解决方案可能会很有吸引力:
实现网络设备的一般外围问题
如前所述,消除这种重复的另一种方法是使用共享库。 假设您的好友使用相同的技术,这应该不会太困难,尽管微服务架构中有关共享库的常见警告适用。
什么时候使用
对于仅提供 Web UI 的应用程序,我怀疑 BFF 只有在您需要在服务器端进行大量聚合时才有意义。 否则,我认为其他 UI 组合技术在没有额外的服务器端组件的情况下也能正常工作(我希望尽快讨论这些)。
但是,当您需要为移动 UI 或第三方提供特定功能时,我会强烈考虑从一开始就为每个使用 bff。 如果部署额外服务的成本很高,我可能会重新考虑,但 BFF 可以带来的关注点分离使其在大多数情况下成为一个非常引人注目的提议。 由于以上原因,如果构建 UI 的人和下游服务之间存在明显的分离,那么我更倾向于使用 BFF。
进一步阅读(和观看)
自从我写这篇文章以来,ThoughtWorks 的 Lukasz Plotnicki 发表了一篇关于 SoundCloud 使用 BFF 模式的精彩文章
Lucas 在最近一期的软件工程播客中接受了关于模式(和其他事情)的采访。
来自 SoundCloud 的 Bora Tunca 在 microxchg 2016 的演讲中也进行了更详细的介绍。
综上所述
前端后端解决了使用微服务时移动开发的一个紧迫问题。 此外,它们为通用 API 后端提供了一个引人注目的替代方案,许多团队将它们用于移动开发以外的目的。 限制他们支持的消费者数量的简单行为使他们更容易处理和更改,并帮助开发面向客户的应用程序的团队保持更多的自主权。
本文:
讨论:请加入知识星球【首席架构师圈】或微信小号【jiagoushi_pro】
(此处已添加圈卡,请前往今日头条客户端查看)