Shopify如何在商店应用上实现服务器驱动的UI架构?


Shopify最近实施了一个被称为服务器驱动的用户界面的架构,使我们能够控制我们想要为商家的商店显示哪些部分。

在实施服务器驱动的用户界面之前,商店屏幕的显示逻辑纯粹是客户驱动的。在客户端-服务器架构的背景下,这意味着商店客户端负责处理用户界面的所有方面,包括商店屏幕的布局、视觉设计元素,以及渲染哪些用户界面组件。

客户驱动架构出现的一个问题是商店布局无法根据个别商家的需求进行定制。例如,对于拥有许多产品的商家,显示“畅销商品”部分是合适的,因为预计一小部分商品会比其他商品卖得更好。然而,对于产品很少的商家,独立的“畅销商品”部分没有意义,因为它会与主要产品列表重复。

鉴于静态的、客户端驱动的 UI 的性质,我们在启动新实验的频率和时间上受到限制,因为我们受到每周发布节奏的约束。另一方面,服务器驱动的 UI 架构允许我们在我们认为必要的时候启动实验,并为商家跨不同的模板和布局运行这些实验。这是因为后端能够控制当前正在运行哪些实验,以及要为商家显示哪些模板和布局。

在服务器驱动的 UI 架构中,服务器端负责通知客户端要呈现的商店部分(例如:畅销商品、新品、所有产品)和布局(网格或货架)。这种服务器驱动的 UI 架构的主要目标是使商家商店能够根据其不同的需求进行个性化设置。这将通过模板化的商店布局来执行,这将呈现商家所需的部分。 

这种架构还允许团队同时为多个商家进行多个实验,以便更好地了解他们的行为。随着时间的推移,这些实验使团队能够制作出最好的商店版本。 
此外,服务器驱动的 UI 架构还确保商店的某些方面不依赖于应用程序的发布。例如,如果在商店中呈现的其中一个部分存在问题,则可以在后端实施更改以确保不会为商家显示该部分,并且可以在未来的应用程序版本中修复该问题。

架构
服务器端的架构由模板处理层、编排器、数据加载层和商店服务器(以前称为 Arrive-Server)GraphQL 层组成。 

以下是每一层的细分:

  • 模板处理层:包含一个硬编码模板,它描述了商店默认模板中包含的部分类型。该层还具有来自商家的自定义项,这些自定义项指定了他们希望在其商店中可见的内容。模板元数据读取器从商户数据库中读取静态模板和商户特定模板,并返回要呈现的部分。
  • 数据加载层SectionDataLoader:在提供所需的部分后,为每个要呈现的部分创建一个对象。该SectionDataLoader对象包含用于获取部分数据的所有逻辑。 
  • GraphQL Layer : 将SectionDataLoader对象映射到相应的 GraphQL 类型,并为 Shop App 解析类型和字段。 
  • 编排层:编排模板处理层、数据加载层和 GraphQL 层之间的整个流程,并为每一层传递预期的信息。
  • 在服务器端,GraphQL API 允许执行商店部分查询,这会返回 shop 商店的部分列表。每个部分都是从基本部分类型构建的,并且一个部分可以进一步定义为ProductsSection或CollectionsSection。该ProductsSection类型用于返回产品列表的部分,例如“所有产品”、“畅销产品”或“趋势”。另一方面,CollectionsSectiontype 用于返回集合列表的部分。还定义了具有可配置大小属性的各种布局类型,例如 GridLayout 和 ShelfLayout。网格布局支持具有大型产品块的 2x2 网格或具有中型产品块的 3x3 网格。货架布局允许用户向右滚动,并支持小型或大型产品块。在部分上拥有此布局属性使我们能够轻松创建新的方式来显示实体,而无需创建全新的部分。

组件的渲染

我们首先有一个名为ServerDrivenStoreScreen的顶层组件,当买家想查看商家的商店时,他们会被导航到这个组件。对于商店屏幕中的每个部分("收藏"、"最佳销售"、"所有产品"),我们渲染StoreSectionContainer组件。在渲染这个组件的同时,ServerDrivenStoreScreen还渲染了商店的标题、导航栏和产品搜索栏。

然后,StoreSectionContainer根据提供给它的部分类型来决定渲染哪个部分。如果部分类型是产品,那么StoreSectionContainer将渲染ProductSection组件。否则,如果部分类型是一个集合,那么StoreSectionContainer将呈现CollectionsSection组件。ProductsSection组件将根据从服务器传来的显示类型渲染ProductShelf或ProductGrid组件。ProductsSection组件还为ProductGrid / ProductShelf组件提供回调函数,当一个产品被按下时,它将导航到ProductDetails页面。

CollectionsSection组件渲染了一个CollectionShelf组件来显示产品的集合。该组件还为CollectionShelf组件提供回调函数,当按下一个集合时,该组件将导航到CollectionDetails页面。ProductGrid组件显示产品的网格,同时也支持大尺寸和小尺寸的显示。最后,ProductShelf和CollectionShelf组件都渲染了一个可以向右滚动的产品或收藏品的货架。

为了获得这些组件所需的所有数据,将进行两个主要的GraphQL调用。第一个GraphQL调用是一个StoreSections查询,获取StoreSections数据,用于渲染商店的所有部分。这个调用是在ServerDrivenStoreScreen中使用useStoreSections钩子进行的,返回的商店部分数据被传递到各个子组件中。

第二个GraphQL调用是一个现有的ShopInfo查询,它预先从缓存中获取数据以填充商店的标题。这个调用是在ServerDrivenStoreScreen中使用useStoreInformation钩子进行的,StoreHeader组件在ServerDrivenStoreScreen中被渲染。