Angular库和微服务的详细指南

我们生活在一个微服务的世界里,而且这个世界将继续存在。后端开发者需要深入研究领域驱动设计,编写无状态、有弹性、高可用的服务,通过变更数据捕获保持数据同步,处理网络故障,处理认证、授权、JWT......并公开一个漂亮的超媒体API,以便前端开发者可以为其添加一个伟大的用户界面。

很好!但是前端怎么办?

假设我们有几个微服务,几个团队,几个API,因此也有几个用户界面。你怎样才能创建一个集成的、一致的用户界面,使你的用户实际上看不到有多少微服务就有多少用户界面?在这篇文章中,我将使用Angular库实现客户端UI组合设计模式。

一个微服务架构

假设我们有一个CD BookStore应用程序,允许客户在线购买CD和书籍,也允许管理员检查库存。我们也有一些复杂的ISBN号码生成器需要处理。如果我们将这个应用程序建模为微服务,我们最终可能会将其分割成3个独立的微服务:

  • 商店:处理显示CD和书籍的信息,允许用户购买。
  • 库存:允许管理员检查仓库中物品的可用性,如果需要,可以购买新的物品。
  • 生成器:在每次创建新书时生成ISBN号码。

我们最终有两个角色(用户和管理员)通过一个用户界面与这3个API进行交互

客户端UI构成

在一个完美的微服务世界里,这3个微服务是独立的,由3个不同的团队开发,因此,有3个不同的用户界面,每个都以自己的速度发布。一方面,我们有3个不同的UI,另一方面,我们希望我们的用户能够浏览到他们所看到的一个单一的、集成的应用程序。有几种方法可以做到这一点,而我在这里描述的一种方法被称为客户端UI组合设计模式。

问题
如何实现一个显示多个服务数据的UI屏幕或页面?

解决方案
每个团队开发一个客户端UI组件,比如Angular组件,实现他们服务的页面/屏幕的区域。一个UI团队负责实现页面骨架,通过组合多个特定服务的UI组件来构建页面/屏幕。

我们的想法是,在客户端将我们的3个用户界面聚合成一个单一的界面,并最终得到类似这样的东西。

如果使用兼容的技术,将几个用户界面聚合成一个单一的界面效果会更好。在这个例子中,我使用Angular,而且只使用Angular来创建三个用户界面和页面骨架。当然,Web Components也非常适合这个用例,但这并不是本文的目的。

Angular库

自Angular CLI 6以来,一个新奇的功能是能够轻松地创建库。回到我们的架构,我们可以说页面骨架是Angular应用程序(CDBookStore应用程序),然后,每个微服务的用户界面是一个库(商店、库存、发电机)。

在Angular CLI命令方面,这就是我们的情况:

\[sourcecode language="shell"\]  
\# 主应用程序叫CDBookStore  
$ ng new cdbookstore -directory cdbookstore -routing true

\# 我们的3个微服务UI的3个库  
$ ng generate library store -prefix str  
$ ng generate library inventory -prefix inv  
$ ng generate library generator -prefix gen  
\[/sourcecode\]

一旦你执行了这些命令,你将得到以下目录结构:

    • src/app/是良好的老式Angular结构。在这个目录中,我们将有CDBookStore应用程序的页面骨架。正如你将看到的,这个骨架页面只是一个侧边栏,用来从一个微服务用户界面导航到另一个:
    • projects/是所有库的目录
    • projects/generator: Generator微服务的用户界面
    • projects/inventory:Inventory微服务的用户界面
    • projects/store: Store微服务的用户界面

骨架页面

骨架页面是为了聚合所有其他的组件。基本上,它只有一个侧边栏菜单(允许我们从一个微服务UI导航到另一个)和一个。它是你可以拥有登录/注销和其他用户偏好的地方。它看起来像这样:

在代码方面,尽管使用了Bootstrap,但没有什么特别之处。有趣的是定义路由的方式。AppRoutingModule只定义了主程序的路由(这里是主页和关于菜单)。

\[sourcecode language="javascript" title="app-routing.module.ts"\]  
const routes:Routes = \[  
{path:", component:HomeComponent},  
{path: 'home', component:HomeComponent},  
{path: 'about', component:AboutComponent},  
\];

@NgModule({  
imports:\[RouterModule.forRoot(routes)\],  
exports:\[RouterModule\]  
})  
export class AppRoutingModule { }  
\[/sourcecode\]

在侧边菜单栏的HTML一侧,我们可以找到导航指令和路由器:

\[sourcecode language="html" title="app.component.html"\]  
<ul class="list-unstyled components">  
<li>  
<a \[routerLink\]="\['/home'\]">  
<i class="fas fa-home"></i>  
首页  
</a>  
<a \[routerLink\]="\['/str'\]">  
<i class="fas fa-store"></i>  
商店  
</a>  
<a \[routerLink\]="\['/inv'\]">  
<i class="fas fa-chart-bar"></i>  
存货  
</a>  
<a \[routerLink\]="\['/gen'\]">  
<i class="fas fa-cogs"></i>  
Generator  
</a>  
/li>  
</ul>  
<!- 页面内容 ->  
<div id="content">  
<router-outlet></router-outlet>  
</div>  
\[/sourcecode\]

正如你在上面的代码中看到的,routerLink指令被用来导航到主要的应用程序组件以及库组件。这里的诀窍是,/home被定义在路由模块中,但不是/str、/inv或/gen。这些路由是在库本身中定义的。

免责声明:我是一个糟糕的网页设计师/开发员。如果有人看到这篇文章,知道JQuery和Angular,并想帮我一把,我很想让侧边栏可以折叠起来。我甚至为你创建了一个问题;o)

现在,当我们点击商店、库存或发电机时,我们希望路由器能够导航到库的组件:

注意路由器之间的父/子关系。在上面的图片中,红色的路由器出口是父级,属于主应用程序。绿色的router-outlet是子节点,属于库。注意,在store-routing.module.ts中,我们使用str有主路径,所有其他的路径都是childs(使用children关键字)。

\[sourcecode language="javascript" title="store-routing.module.ts" highlight="3″\]  
const routes:Routes = \[  
{  
path: 'str', component:StoreComponent, children:\[  
{path:", component:HomeComponent},  
{path: 'home', component:HomeComponent},  
{path: 'book-list', component:BookListComponent},  
{path: 'book-detail', component: BookDetailComponent}:BookDetailComponent},  
\]  
},  
\] 。

@NgModule({  
imports:\[RouterModule.forRoot(routes)\],  
exports:\[RouterModule\]  
})  
export class StoreRoutingModule {  
}  
\[/sourcecode\]

有一个父/子路由器关系的好处是,你可以从 "功能 "导航中获益。这意味着,如果你导航到/home或/about,你会留在主程序中,但如果你导航到/str、/str/home、/str/book-list或/str/book-detail,你会导航到Store库。你甚至可以在每个功能的基础上进行懒惰加载(只在需要时加载商店库)。

Store库在顶部有一个导航条,因此它需要routerLink指令。注意,我们只需要使用子路径[routerLink]="['book-list']"而不需要指定/str(不需要[routerLink]="['/str/book-list']")。

\[sourcecode language="html" title="商店。html"\]  
<nav>  
<div class="collapse navbar-collapse">  
<ul class="navbar-nav mr-auto">  
<li class="nav-item dropdown">  
<a class="nav-link dropdown-toggle">Items</a>  
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">  
<a class="dropdown-item" \[routerLink\]="\['book-list'\]">书籍</a>  
<a class="dropdown-item" \[routerLink\]="\['cd-list'\]">CD</a>  
<a class="dropdown-item" \[routerLink\]="\['dvd-list'\]">DVDs</a>  
</div>  
</li>  
</ul>  
</div>  
</nav>  
<!- 页面内容 ->  
<div id="content">  
<router-outlet></router-outlet>  
</div>  
\[/sourcecode\]

优点和缺点

客户端UI组合设计模式有一些优点和缺点。如果你已经有了每个微服务的用户界面,而且每个微服务都使用了完全不同的技术(Angular vs React vs Vue.JS vs ...)或不同的框架版本(Bootstrap 2 vs Bootstrap 4),那么聚合它们可能会很麻烦,你可能不得不重写一些片段以使它们兼容。

但这实际上是有好处的。如果你没有成百上千的开发团队,你可能最终会成为从一个团队转移到另一个团队的人,并且会很高兴发现(或多或少)相同的技术。这在底盘模式中被定义。对于你的最终用户来说,应用程序将具有相同的外观和感觉(在手机、笔记本电脑、桌面、平板电脑上),这给人一种整合的感觉。

以Angular库的结构方式,拥有一个单一的repo要容易得多。当然,每个库都可以有自己的生命周期,并且是独立的,但在一天结束时,把它们放在同一个 repo 上会更容易。我不知道你是否喜欢MonoRepos,但有些人喜欢;o)

结语

我们架构和开发微服务已经有几十年了(是的,它曾经被称为分布式系统,或分布式服务),所以我们大致知道如何让它们在后端工作。现在,我们面临的挑战是,如何让一个用户界面与不同团队开发的多个微服务进行沟通,同时让终端用户感到一致和集成。你可能并不总是有这种需要(看看亚马逊是如何为其服务提供完全不同的用户界面的),但如果你有这种需要,那么客户端UI组合设计模式就是一个很好的选择。

如果你想尝试一下,请下载代码,安装并运行它(只有前端,没有后端API)。不要忘记给我一些反馈。我很想知道你是如何将几个用户界面聚合成 "一个 "的。

猜你喜欢

转载自juejin.im/post/7126032346213515294