微服务前端-分离前端的现代方法

太胖的温泉


长期以来,微服务架构一直是大型和复杂系统开发中的事实上的标准。 它具有许多优点:它是对模块的严格划分,较弱的连接性,对故障的抵抗力,逐步退出生产以及组件的独立版本控制。

确实,经常说到微服务架构,只提到了后端架构,而前端仍然是单一的。 事实证明,我们提供了很好的支持,而前沿则使我们退缩。

今天,我将告诉您我们如何在SaaS解决方案中进行微服务,以及遇到了哪些问题。

发行


最初,我们公司的开发看起来像这样:微服务的开发涉及许多团队,每个团队都发布自己的API。 并且有一个单独的团队使用不同的微服务的API为最终用户开发SPA。 通过这种方法,一切都可以工作:微服务开发人员了解有关其实现的所有信息,SPA开发人员了解用户交互的所有复杂性。 但是有一个问题:现在每个前端都应该知道所有微服务的所有复杂性。 微服务越来越多,前端提供商也越来越多-随着团队内部专业化的出现,即互换性和通用性消失,敏捷开始崩溃。

因此,我们进入了下一个阶段-模块化开发。 前端团队分为多个子命令。 每个人都负责其应用程序的一部分。 它已经变得越来越好,但是随着时间的流逝,这种方法已被耗尽,原因有几个。

  • 所有模块都是异构的,具有各自的特点。 对于每个模块,其自身的技术都更适合。 同时,在SPA条件下,技术的选择是一项艰巨的任务。
  • 由于SPA应用程序(在现代世界中,这意味着编译为一个捆绑包或至少一个程序集),因此只能同时发布整个应用程序。 每次引渡的风险都在增加。
  • 依赖性管理变得越来越困难。 不同的模块需要不同的(可能是特定的)依赖项版本。 某人尚未准备好切换到更新的依赖项API,并且由于旧的依赖项分支中的错误,某人无法制作功能。
  • 由于第二点,必须同步所有模块的发布周期。 每个人都在等待落后者。

削减前端


关键时刻积累了片刻,他们决定将前端划分为……前端微服务。 让我们定义什么是前端微服务:

  • UI的完全隔离部分,绝不依赖于其他部分; 根本隔离 从字面上看是作为独立应用程序开发的;
  • 每个前端微服务从头到尾负责一系列特定的业务功能,也就是说,它本身是完整的功能;
  • 可以用任何技术编写。

但是我们走得更远,引入了另一个层次的划分。

片段概念


我们将一个片段称为由js + css + 组成的包。 实际上,这是UI的独立部分,必须遵守一组开发规则,以便可以在常规SPA中使用。 例如,所有样式应针对片段尽可能具体。 请勿尝试直接与其他片段进行交互。 您必须具有一个特殊的方法,可以将片段绘制到的DOM元素传递给该方法。

由于有了描述符,我们可以保存有关环境的所有已注册片段的信息,然后可以通过ID来访问它们。

这种方法使您可以将在不同框架上编写的两个应用程序放在一页上。 它还使编写通用代码成为可能,这将使您能够在页面上动态加载必要的片段,对其进行初始化并管理生命周期。 对于大多数现代框架而言,只要遵循“卫生规则”即可。

如果片段没有机会与同一页面上的其他人“同居”,则有一个后备脚本,我们可以在其中使用片段在iframe中绘制片段(解决相关问题的方法不在本文的讨论范围之内)。

想要使用页面上现有代码段的开发人员所要做的就是:

  1. 将微服务平台脚本连接到页面。
     <script src="//{URL to static cache service}/api/v1/mui-platform/muiPlatform.js"></script> 

  2. 调用将片段添加到页面的方法。

     window.MUI.createFragment( // fragment name "hello-label", // fragment model { text: "HelloLabelFragment text from run time" }, // fragment position { selector: ".hello-label-placeholder", position: "afterend" }) .then(callback); 


另外,对于片段之间的通信,有一个基于Observablerxjs的总线。 它是用NativeJS编写的。 此外,SDK随附了用于各种框架的包装程序,这些包装程序有助于本地使用此总线。 Angular 6的一个示例是返回rxjs/Observable的实用程序方法:

 import {fromEvent} from "@netcracker/mui-platform/angular2-factory/modules/shared/utils/event-utils" fromEvent("<event-name>"); fromEvent(EventClassType); 

此外,该平台还提供了一组服务,这些服务通常由不同的片段使用,并且是我们基础结构中的基础。 这些服务包括本地化/国际化,授权服务,与跨域Cookie一起使用,本地存储等。 对于它们的使用,SDK还提供了各种框架的包装器。

结合前端


例如,我们可以在SPA管理区域中考虑这种方法(它结合了来自不同微服务的不同可能设置)。 我们可以使每个书签的内容成为一个单独的片段,每个微服务将分别交付和开发。 因此,我们可以创建一个简单的“标题”,在单击书签时将显示相应的微服务。

图片

我们提出了碎片的想法


用一个片段开发一个书签并不总是使我们能够解决所有可能的问题。 通常需要在一个微服务中开发UI的特定部分,然后将其在另一个微服务中重用。

这些碎片也可以帮助我们! 由于所有片段需求都是用于渲染的DOM元素,因此我们为任何微服务提供了一个全局API,通过它可以将任何片段放入其DOM树中。 为此,只需传递片段ID和需要在其中绘制它的容器即可。 其余的将由自己完成!
现在,我们可以构建任何嵌套级别的“嵌套娃娃”,并重复使用整个UI,而无需在多个地方提供支持。

经常发生的情况是,在一页上有多个片段,在更改页面上的某些常用数据时应更改其状态。 为此,他们拥有一个全局(NativeJS)事件总线,通过该总线可以进行通信并响应更改。

图片

共享服务


在微服务架构中,不可避免地会出现中央服务,而其他所有人都需要这些数据。 例如,存储翻译的本地化服务。 如果每个微服务都开始分别爬到该数据到服务器,则在初始化期间我们只会收到一堆请求。

为了解决此问题,我们开发了NativeJS服务的实现,这些实现提供对此类数据的访问。 这使得不发出不必要的请求和缓存数据成为可能。 在某些情况下,甚至可以预先将此类数据输出到HTML页面中,以完全摆脱请求。

此外,在我们的服务上针对不同框架开发了包装器,以使其使用非常自然(DI,固定接口)。

前端微服务的优点


从整体中分割出来的最重要的事情是能够为每个团队分别选择技术和透明的依赖管理。 而且,它提供了以下内容:

  • 非常明确地划分了责任范围;
  • 独立发行:每个片段可能都有自己的发行周期;
  • 提高整体解决方案的稳定性,因为单个片段的发行不会影响其他片段;
  • 轻松回滚功能,将其部分向受众推出的能力;
  • 该片段很容易放置在每个开发人员的头部,这导致了
团队成员的互换性; 此外,每个前端都可以更好地了解与相应后端交互的所有复杂性。

带有微seris前端的解决方案看起来不错。 的确,现在每个片段(微服务)都可以自行决定如何部署:是否只需要nginx分发静态信息,成熟的中间件以将请求聚合到支持或支持websocket或其他一些特定细节(以HTTP内的二进制数据传输协议的形式)。 此外,片段可以选择自己的组装方法,优化方法等。

前端微服务的缺点


美中不足的是,您永远做不到。

  • 片段之间的相互作用无法通过标准试管方法(例如DI)来确保。
  • 共享依赖关系怎么办? 毕竟,如果不从碎片中取出应用程序,其大小将跨越式增长。
  • 无论如何,在最终应用程序中只有一个人负责路由。
  • 如果片段之一无法访问/无法绘制该怎么办。
  • 目前尚不清楚该如何处理不同的微服务可以位于不同的域这一事实。

结论


我们在这种方法上的经验证明了它的可行性。 生产中特征的输出速度已大大提高。 接口各部分之间的隐式依赖关系数量减少到几乎为零。 我们有一个一致的用户界面。 您可以安全地测试功能,而无需大量人员参与。

不幸的是,在一篇文章中,很难涵盖在重复这种架构的过程中可能发现的所有问题和解决方案。 但是对我们而言,利弊显然大于弊端。 如果Habr有兴趣透露这种方法的实现细节,我们将撰写续集!

Source: https://habr.com/ru/post/zh-CN420753/


All Articles