在微服务环境中使用 Angular 2+

Jen*_*nsB 6 single-page-application microservices angular

在过去的几年里,我已经构建了几个 angular(2+ 打字稿)应用程序并且已经非常习惯了。然而,在我参与的一个项目中,我们正在使用微服务架构,该架构鼓励将所有内容尽可能拆分成小的可用(和合理)部分,然后将其加载到主页中,并且可以相互独立地执行操作。

如果这是一个没有交互式内容的常规 HTML 页面,这将很容易做到。但在这种情况下,我们可以有一个页面:搜索功能、列表功能和详细信息模式。这些东西都需要能够相互交互并对事件做出反应,最好充当 SPA,在导航时不需要页面加载。

在一个经典的 Angular 应用程序中,我会使用模块和组件(反过来可以使用公共共享组件并且所有组件都可以延迟加载)来构建它,并在单个 Angular 应用程序中设置共享路由。但是如何在保持微服务标准/架构的同时做到这一点?这对前端来说甚至是可取的吗?人们可以将 Angular 中的模块视为一种微服务,但这是否足够好?

我找到了诸如https://single-spa.js.org/ 之类的库。这是解决这个问题的方法吗?创建多个小应用程序并将它们与上面的另一个框架缝合在一起?然而,这将需要更多的带宽,因为前端需要多次下载而不是一次。

乍一看,这一切似乎都使任务复杂化,但收效甚微。特别是因为在常规 Angular 应用程序中,应用程序的一部分可能会停止工作,而不会真正影响 Angular SPA 的另一部分(这是 MS 的主要目标之一)。是否有一些标准化的指标可以用来查看何时创建单个 SPA 或使用拆分的微服务变体更好?

Ger*_*son 11

概括

微服务后端不需要微服务前端。您可能希望按模块组织您的 angular 应用程序,除非您有一个特别大的应用程序,它会更好地作为多个较小的独立应用程序。在这种情况下,Angular 可能不是最适合您的框架。


您不应该仅仅因为服务器端应用程序是这样构建的,就选择以某种方式构建前端应用程序。在前端使用单个 Angular 应用程序在后端使用微服务是完全可行的。但是如果你想把它分开并且这样做有意义,你应该首先考虑使用多个模块。

您已经在使用模块(这些模块已添加到您的 app.module 文件的导入中),例如 HttpClientModule,因此创建自己的模块并不是一个很大的步骤。模块将包含与应用程序的一部分相关的组件,例如特定功能或一组 UI 组件。

模块不会立即提供任何技术优势,但从开发人员的角度来看,它们有助于组织大型代码库。但是,您可以选择延迟加载这些以加快应用程序的初始加载时间,并预加载延迟加载的模块,以便应用程序在加载初始页面后加载其他模块。最初,您可能希望将所有模块保存在一个代码库中,这样可以更轻松地管理代码。随着应用程序的增长,您可以将模块拆分为单独的存储库,如果您到了这似乎是一个好主意的地步。然后可以将它们与您的包管理器 (NPM) 一起安装并像导入其他第三方模块一样导入,但您仍然拥有一个应用程序。

使用这种方法您仍然可以享受单页应用程序的好处,但它更加模块化。在极端情况下,您可以将其拆分为由不同服务器路由提供服务的完全不同的应用程序。选择哪个选项取决于您的要求,但以下内容可能会有所帮助。

单模块

  • 一个应用程序,一个代码库,一个模块。
  • 将此选项用于功能有限的小型简单应用程序。

多个模块

  • 一个应用程序但多个模块。
  • 代码更有条理,对开发人员更友好
  • 模块可以独立加载,减少初始加载时间。
  • 对于较大的应用程序或应用程序至少有两个不同的部分,请使用此选项。

多种应用

  • 多个应用程序,每个应用程序都可以用一个或多个模块构建。
  • 模块可以作为外部依赖项在应用程序之间共享。
  • 如果应用程序的各个部分完全分开,请使用此选项。
  • 您将需要在此处合并传统多页应用程序的各个方面,并使用服务器进行(某些)路由。您将有额外的非角度复杂性;一个应用程序将如何与另一个应用程序共享数据?
  • 如果你不知道你是否需要这个,你可能不需要。

99% 的 Angular 项目应该是组织为多个模块的单个应用程序。鉴于您的问题是特定于 Angular 的,我创建了一个带有路由的简单应用程序,然后将其拆分为多个延迟加载的模块,以便您进行比较。

这是标准的单模块应用程序。

这是它的一个分支,具有完全相同的功能,但分为多个模块。

我使用了功能模块,以及一个共享模块。

功能模块

与特性相关的所有组件、指令等都包含在特性模块中。您可以看到 AppModule 在多模块版本中更加精简。

每个模块都应该有自己的路由器(如果功能有路由)。所有对延迟加载模块的引用都应该从 AppModule 中删除,以防止 Webpack 将它们包含在主包中,这会导致它们被立即加载。然后,模块路径的字符串表示可用于指向模块:

const appRoutes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'places', loadChildren: './places/places.module#PlacesModule' },
  { path: 'products', loadChildren: './products/products.module#ProductsModule' }
]
Run Code Online (Sandbox Code Playgroud)

有不同的预加载策略来确定延迟加载模块的加载方式。PreloadAllModules 将在加载初始模块后加载所有延迟加载的模块。

imports: [
  RouterModule.forRoot(appRoutes, { preloadingStrategy: PreloadAllModules })
],
Run Code Online (Sandbox Code Playgroud)

共享模块

共享模块用于使组件指令等可用于多个模块。他们需要导出将被其他模块使用的任何声明,以及 CommonModule:

exports: [
  CommonModule,
  LegalComponent
]
Run Code Online (Sandbox Code Playgroud)

服务

服务也可以提供到单独的模块中。当提供给延迟加载模块或延迟加载模块中使用的共享模块时,您将获得服务的不同实例。因此,为了让示例保持简单,我将服务留在 AppModule 提供程序数组中。