本文翻译自PhilCalçado的官网:https://philcalcado.com/2015/09/18/the_back_end_for_front_end_pattern_bff.html

对我们的架构演变保持透明是我们技术战略的一部分。我们在无数场合谈过的但从未真正详细描述过的东西是我们应用后端用于前端架构模式或BFF。这篇文章记录了我对如何开发和应用这种技术的理解。

我对软件组件演变的理解

在完全分布式架构变得可行之前,组织通常会在一个或多个层中构建应用程序。层是应用程序的高度耦合但相当独立的组件。据加上在这,而不是服务,它被认为仅由一个应用程序使用的感觉。它独立于如何不作为同一过程的一部分运行,甚至通常不在同一台机器中运行。

让我们用三个虚构的应用来说明这一点,当时任何大公司都会发展出来:

这些体系结构可能变得非常复杂,但总体而言,在不同的应用程序之间绘制线条非常容易,清楚地划分出一个开始和另一个结束的位置。

当时,每个应用程序都有自己的数据副本和重复的常见业务流程实现。随着时间的推移,随着组织获得或构建越来越多的应用程序,我们意识到我们需要不同的东西。我们需要应用程序来共享数据和重用逻辑,而我们曾经简单的架构变得有点复杂:

随着对更多重用和整合的需求,软件行业的集体思维方式决定了一个非常抽象的概念,称为服务。实际上,这意味着上面的图表改为类似于此的东西:

上述架构的卖点是这些可重用服务提供的灵活性。理论上,在这个平台上构建应用程序现在是一个问题:

  1. 选择您需要的服务
  2. 写一些调用这些服务的胶水代码
  3. 将从中获得的数据合并到最终用户更熟悉的内容中
  4. 以最终用户可以使用的方式呈现此数据

与此同时,计算机和互联网正变得越来越流行。过去与职员或系统操作员交互的客户开始直接与应用程序本身交互。设计思维和用户体验研究使我们摆脱了复杂的用户界面,专注于让专家用户更高效地获得更丰富,更实用的体验,客户可以理解这些体验 - 没有人阅读网站手册。更丰富的经验需要丰富的数据,这意味着汇总来自各种来源的信息。

按照我们的示例,我们最终会得到如下图所示的内容。

我们不再拥有业务线系统的用户界面,而是越来越多的用户界面本身就是应用程序。这些应用程序通常用JSP,PHP或ASP编写,其代码包含用户界面和特定于应用程序的后端逻辑。

打破巨石

上面简单的例子与许多现代技术组织的架构发展方式没有什么不同。2011年,SoundCloud的网站如下所示:

Logic和All逻辑在一个地方。有一个系统,而这个系统是应用程序。

前一篇文章所述,我们发现此架构存在许多问题,并决定将逻辑提取到微服务中。尽管我们在提取后端服务方面取得了成功,但最长时间内母舰仍然处于每个请求的关键路径上。

我们正在进行的架构改变背后的主要动机是缩短新功能的上市时间,我们发现我们最糟糕的瓶颈在于任何必须触及整体的变化。考虑到用户界面更改的频率,从整体中提取代码是一种提高生产力的直观方法。然后,我们在其自己的组件中提取了我们的UI层,并使其从我们的公共API中获取数据:

早在2011年,当这些架构发生变化时,绝大多数用户都在网上。正如Fred Wilson所预测的那样,最终这种情况发生了变化,我们的用户群开始使用移动应用程序的方式比Web界面更频繁。SoundCloud已经为Android和iOS提供了很长时间的移动客户端,与我们的新Web应用程序类似,他们直接与我们的公共API进行了对话。

dogfooding带来的挑战

在现代软件工程中,狗食通常被认为是一件好事。在我们自己的API之上构建我们的产品被认为是确保我们的API具有高质量并且始终是最新的最佳方式。在实践中,我们遇到了这种方法的几个问题。

我们的第一个问题不一定与技术有关,而是产品开发的根本挑战。如果我们仅使用公共API,那么我们的平台中没有任何内容可供第三方API客户端使用。尽管我们想要一个蓬勃发展的SoundCloud集成生态系统,但我们还是一家广告公司,因此我们需要确保人们使用我们的属性,而不仅仅是我们的数据。创建我们自己的应用程序独有的功能意味着我们必须在许多地方不断检查OAuth范围,并使人们很难欺骗我们的“官方应用程序”密钥。

在一个更技术性的问题上,我们的公共API几乎按照定义是非常通用的。为了使第三方开发人员能够构建有趣的集成,您需要设计一个不会假设数据将如何使用的API。这会产生非常细粒度的端点,然后需要对多个不同端点的大量HTTP请求才能呈现最简单的体验。您可以在下面看到我们过去在整体时代提出的请求数量与我们为新的Web应用程序生成的请求数量:

要生成该单个配置文件页面,我们必须对不同的API端点进行多次调用,例如:

  • GET /tracks/1234.json (该曲目的作者)
  • GET /tracks/1234/related.json (推荐相关的曲目)
  • GET /users/86762.json (关于该曲目的作者的信息)
  • GET /users/me.json (有关当前用户的信息)
  • ...

...然后,Web应用程序将合并以创建用户配置文件页面。虽然这个问题存在于所有平台上,但对于我们不断增长的移动用户群来说,情况更糟,因为他们经常使用不可靠且速度慢的无线网络

我们在上面的体系结构中遇到的第三个甚至更烦人的问题是,即使没有整体结构,我们仍然存在API的瓶颈。每当团队需要更改现有端点时,我们都需要确保更改不会破坏任何现有客户端(包括重要的第三方集成)。每当我们添加新内容时,我们都需要投入大量时间来确保新端点不会过度专用于特定应用,所有客户都可以轻松使用它们。所有这些协调使我们的日常工作变得比应有的困难,并使我们几乎不可能进行A / B测试并减慢新功能的推出。

前端模式的后端(BFF)

在上述架构首次亮相近一年后,我们开始准备开发新的iOS应用程序。这是一个庞大的项目,最终会改变所有房产的用户体验。如此高风险,开发过程中的实验和迭代至关重要。当工程团队开始考虑应用程序的体系结构时,我们发现上述挑战将成为项目的阻碍,我们需要重新思考我们的工作方式。

我们首先提出的解决方案是为移动和网络提供不同的API。我们的想法是,让客户拥有API的团队可以让他们更快地移动,因为它不需要部件之间的协调。我们最初的想法是为不同的前端设置不同的后端。BFF一词是由我们的网络技术主管Nick Fisher创造的(我最初的建议是BEFFE,但我们讲荷兰语的队友否决了这个选项)。

在它的第一个版本中,这些后端看起来仍然看起来像公共API,许多通用端点需要来自客户端的许多调用来呈现单个屏幕。但是,随着时间的推移,我们发现了一些有趣的事情。以用户配置文件页面为例,以前这是一个仅存在于客户端的概念。Web或移动应用程序将从各个端点获取数据,并使用它来创建我们称为用户配置文件的对象。这是一个特定应用程序的对象。

在某些时候,我们的客户团队意识到,由于他们拥有API,他们可以将这个对象推向API。他们可以提取所有调用不同服务的逻辑,并将它们一起混合到后端的用户配置文件中。

这最终将简化代码并提高性能。不是对上面描述的多个端点进行多个不同的调用,而是需要请求的所有客户端都是单个资源:

  • GET /user-profile/123.json

当我们进一步尝试这个模型时,我们发现自己在BFF中编写了很多演示模型。在这个阶段,我们意识到BFF不是应用程序使用的API 。BFF是申请的一部分。

最终,我们的所有属性(包括API)都开始遵循此模式。

一直往下

在某些时候,我们生产了大约五种不同的BFF,我们已经开始研究如何进一步提高生产率。接下来是我们的用户配置文件示例,对我们来说显而易见的是,鉴于每个应用程序都有一个等效的用户配置文件页面,所有BFF都有很多重复的代码来获取和合并它们的数据。

复制并不准确,像网络浏览器这样的大屏幕在其用户个人资料页面上的信息要比微型移动应用程序多得多。然而,我们看到重复是一种难闻的气味,表明我们在域模型中缺少一个对象。为了解决这个问题,我们创建了一个UserProfileService处理这个重复逻辑的方法。

随着时间的推移,我们发现了越来越多这样的情况。我们开始有意识地转向一个架构,在这个架构中,用户理解的大多数核心对象都有自己的微服务支持它们。

架构设计系列-前端模式的后端(BFF)翻译PhilCalçado的更多相关文章

  1. Junit4 架构设计系列(2): Runner.run()与Statement

    Overall 系列入口: Junit4 架构设计系列(1): Request,ClassRequest 和 RunnerBuilder 前文中,我们基本理清了Junit4执行Case大体上的Flow ...

  2. Junit4 架构设计系列(1): Request,ClassRequest 和 RunnerBuilder

    Overall Junit的成功已不言而喻,其广泛应用于单元测试,测试驱动开发领域.大量的工具,IDE都集成了JUnit,著名的有Maven,Ant,Eclipse,甚至像Google SDK提供的A ...

  3. .NET应用架构设计—表模块模式与事务脚本模式的代码编写

    阅读目录: 1.背景介绍 2.简单介绍表模块模式.事务脚本模式 3.正确的编写表模块模式.事务脚本模式的代码 4.总结 1.背景介绍 要想正确的设计系统架构就必须能正确的搞懂每个架构模式的用意,而不是 ...

  4. 爱上MVC系列~前端验证与后端数据有效性验证

    回到目录 有一句话,在10年前就是真理,到现在也一直都是,“前端验证可以没有,但后端验证必须要有”,这句话相信大家都没有意见吧,前端验证一般指通过JS方式实现的,友好的,个性的验证方式,而后端验证是指 ...

  5. .NET应用架构设计—工作单元模式(摆脱过程式代码的重要思想,代替DDD实现轻量级业务)

    阅读目录: 1.背景介绍 2.过程式代码的真正困境 3.工作单元模式的简单示例 4.总结 1.背景介绍 一直都在谈论面向对象开发,但是开发企业应用系统时,使用面向对象开发最大的问题就是在于,多个对象之 ...

  6. .NET应用架构设计—工作单位模式(摆脱程序代码的重要思想,反击DDD)

    阅读文件夹: 1.背景介绍 2.过程式代码的真正困境 3.工作单元模式的简单演示样例 4.总结 1.背景介绍 一直都在谈论面向对象开发.可是开发企业应用系统时.使用面向对象开发最大的问题就是在于,多个 ...

  7. Unity应用架构设计(1)—— MVVM 模式的设计和实施(Part 2)

    MVVM回顾 经过上一篇文章的介绍,相信你对MVVM的设计思想有所了解.MVVM的核心思想就是解耦,View与ViewModel应该感受不到彼此的存在. View只关心怎样渲染,而ViewModel只 ...

  8. iOS架构设计系列之解耦的尝试之变异的MVVM

    最近一段时间,在思考如何合理的架构一个可扩展性良好的界面编程方式.这一部分的成果做成了一个叫ElementKit的库.目前功能在不断的完善中. 关于iOS的架构,看多了MVVM,VIPER,MVC,M ...

  9. 图解 kubernetes scheduler 架构设计系列-初步了解

    资源调度基础 scheudler是kubernetes中的核心组件,负责为用户声明的pod资源选择合适的node,同时保证集群资源的最大化利用,这里先介绍下资源调度系统设计里面的一些基础概念 基础任务 ...

随机推荐

  1. 2、Hibernate持久化编写

    一.对于hibernate中的PO编写规则: 1. 必须提供一个无参数的public构造方法   2. 所有属性要private ,对外提供public 的get/set方法   3. 在PO类必须提 ...

  2. MVC过滤器:自定义异常过滤器使用案例

    在上一篇文章中讲解了自定义异常过滤器,这篇文章会结合工作中的真实案例讲解一下如何使用自定义异常过滤器. 一.需求 本案例要实现的功能需求:在发生异常时记录日志,日志内容包括发生异常的Controlle ...

  3. nikto---基本使用

    目录 一:基本使用 二:调节扫描过程 三:命令行选项 四:配置文件 注意:使用版本:Nikto v2.1.6 功能:Web服务器评估工具,目的在于查找任何类型的Web服务器的各种默认和不安全的文件,配 ...

  4. 漫谈golang设计模式 工厂模式

    工厂模式 意义:创建过程交给专门的工厂子类去完成.定义一个抽象的工厂类,再定义具体的工厂类来生成子类等,它们实现在抽象按钮工厂类中定义的方法.这种抽象化的结果使这种结构可以在不修改具体工厂类的情况下引 ...

  5. 9.智能快递柜SDK(串口型锁板)

    1.智能快递柜(开篇) 2.智能快递柜(终端篇) 3.智能快递柜(通信篇-HTTP) 4.智能快递柜(通信篇-SOCKET) 5.智能快递柜(通信篇-Server程序) 6.智能快递柜(平台篇) 7. ...

  6. [b0003] 总览:Hadoop 个人学习路线进展

    3.   Spark 搭建  过 1.1   搭建伪分布式2.0.1  ok 2016-10-23  耗时 2h ref  [0006] Spark 2.0.1 伪分布式搭建练手 后续: 1.2 分布 ...

  7. Oracle Merge Into 使用注意事项

    我们操作数据库的时候,有时候会遇到insertOrUpdate这种需求.如果数据库中存在数据就update,如果不存在就insert.Orcale数据库都提供了 MERGE  INTO 方法来处理这种 ...

  8. (办公)vue下载excel,后台用post方法

    后台方法的参数必须是@RequestBody修饰的. 前台关键代码: axios ( { method : 'post', url : api.exportPlayTime , // 请求地址 dat ...

  9. 爬虫---lxml爬取博客文章

    上一篇大概写了下lxml的用法,今天我们通过案例来实践,爬取我的博客博客并保存在本地 爬取博客园博客 爬取思路: 1.首先找到需要爬取的博客园地址 2.解析博客园地址 # coding:utf-8 i ...

  10. 计算多个点中距离最远的两个点 python

    import numpy as npfrom scipy import spatial print("hello")# test pointspts = np.random.ran ...