作为Virta体验与参与团队的全栈软件工程师,我帮助开发愉快的体验,让患者参与我们的糖尿病逆转治疗。Virta用户群的指数级增长是我们团队面临的主要挑战之一:扩展教育和励志内容的交付。特别是,由于患者有各种各样的需求,我们需要支持一个大型的内容语料库,用于继续教育和掌握。患者查看这些内容的主要界面称为Discover,它以前被称为Resource Center。Discover包括数百篇自行发表的文章、网络研讨会、食谱和指南。
在过去,这些内容主要是通过名为content management system (CMS)的内容管理系统托管和显示的Wordpress,内容显示在我们的移动应用程序使用WebView.这种设置允许我们的临床和内容策略团队创建和共享新内容,而无需在初始设置后进行任何工程工作。虽然在过去几年里这是一个可行的系统,但在去年,随着我们的发展加速,一些已知的功能限制变得更加紧迫。
主要现有限制:
主要期望改进:
此外,我们现有的提供个性化内容的流程是不可扩展的,因为教练手动为每个患者找到最佳内容非常耗时。这些对我们内容管理系统的期望改进还将使我们能够使用软件推荐个性化内容。这创造了一种更吸引人的用户体验,因为算法可以使用关于已知用户偏好的数据,比人类更准确地提出建议。
在初创公司的早期阶段,以快速迭代的名义严重依赖外部服务通常是有价值的,因此我们在可行的情况下对软件采取“购买”策略。放弃一些控制措施或设计来使用开箱即用的解决方案有时是快速移动的能力的必要权衡,而不是自己构建所有的解决方案。这就是我们第一次选择WordPress时的情况,它确实为我们服务了很多年。然而,在某些时候,拥有更多的控制权变得越来越可取。在我们的例子中,我们现在觉得控制我们的内容数据、交付和表示将使我们能够构建更丰富的体验。它还将帮助我们更好地了解用户如何与内容互动,使我们的应用人工智能团队能够使用这些数据以及自然语言处理算法来建议与个人相关的内容。因此,我们决定转向一种混合的“购买+构建”策略,我们仍然利用外部软件,但会寻找具有更大灵活性的选项来集成自构建软件,以创建所需的体验。
一旦我们确定Virta需要为我们的内容建立一个新的家园,我们确定了以下需求:
基于我们的技术堆栈和上述要求,Sanity.io被选为最佳解决方案。
在像Wordpress这样的传统CMS中,从后端、内容管理到表示层的所有内容都由CMS处理。理智,另一方面,是一种无头CMS.在无头CMS中,内容表示(头部)不包括在内。内容是通过API提供的,因此应该是表示不可知的。本质上,平台处理数据,而我们处理表示。虽然这种模式需要在前端(头部)进行大量的前期投资,但它可以灵活地在应用中呈现内容,并提供一个平台来构建更吸引人的体验。
完整性允许我们使用JavaScript对象定义内容的结构。在Sanity中,每一块面向用户的内容都被称为文档。文档的结构是在模式中定义的,通过组合简单的字段(如Text、Image、Number和Date),以及更复杂的类型(如Objects)。对象是用于定义自定义类型的字段的集合。
使用对象和文档,我们可以为内容定义一个结构。例如,一个recipe由几个简单的字段组成:
我们可以进一步分解这些字段,以获得更好的可重用性。在下面的例子中,我们定义了一个成分文档。文档是可以重用的单个内容。我们可以创建一个橄榄油的成分文档,标题为“橄榄油”。一旦创建,我们可以在多个食谱中引用这种成分。
这是一个非常简单的文档,它只包含一个字段-成分的名称。在食谱中,我们需要知道一种原料的名称以及它的数量。一个量包括一个数字和一个测量单位,如“5克”。我们可以在以下对象中定义它:
注意:与文档不同,对象的数据不可重用,也不能被引用。对象允许我们通过将字段分组在一起来定义复杂类型。在我们的例子中,重复使用“橄榄油”作为成分可能是有用的,但对于“2汤匙”这样的数量来说,这是不必要的。
现在我们可以将两者结合起来,创建一个recipeIngredient字段。这里,我们引用一个成分文档。这意味着,如果所引用的成分得到更新,那么所有引用它的文档都将自动反映该更改。
注意:如上所述,“ingredient”是一个对成分文档的引用字段,而“quantity”是一个对象:描述某物的字段分组。
现在我们可以在配方模式中使用这个recipeIngredient字段:
注意:Quantity对象还用于捕获食谱的准备时间和总时间字段,因为这些字段本质上可以分解为一个数字和一个单位,如“30分钟”。
使用开箱即用的类型(如字符串、图像和数组),以及我们自己的自定义类型(如recipeIngredient),我们已经定义了一个现在可以用于所有食谱的模板。
也许最有趣的字段类型是块,它提供了模仿的富文本功能便携式文本.此富文本以JSON对象数组的形式存储在数据库中,可以以多种不同的方式(如HTML或React组件)表示。
在Virta,我们有一个用React Native编写的组件库。用理智的block-content-to-reacthelper,我们可以使用在整个应用程序中显示内容时使用的相同的React组件。这为我们的用户提供了一个有凝聚力的体验,因为内容看起来和感觉就像应用程序的其余部分一样。
我们的新内容平台的要求之一是根据特定的用户属性对内容进行细分。随着Virta的用户基础不断增长和多样化,他们的需求也在不断增长。2021年,我们推出了Virta诊所扩展,最终推出了我们的第一批糖尿病管理用户,他们需要不同于糖尿病逆转患者的内容。出于这个原因,我们必须想出一种可扩展的方法,根据这些不同的用户护理计划(管理或逆转)来分割我们的内容。理智允许我们通过简单地扩展我们的内容模型来做到这一点。通过向文档模型添加护理协议字段,我们能够在查询中轻松地筛选这些属性。展望未来,我们不需要做任何工程工作来分割额外护理协议的内容,因为我们的内容团队只需要适当地对内容进行分类。
我们的新平台的另一个要求是能够支持多种语言的内容。我们通过使用理智的插件整理翻译文件。使用插件,我们为文档级翻译奠定了基础。这意味着对于我们拥有的每个英语文档,我们可以创建引用原始文档的另一种语言的翻译。
我们考虑的另一种方法是字段级翻译,顾名思义,就是在单个字段级上实现翻译,而不是在整个文档上实现翻译。虽然这种方法比文档级翻译更灵活,但它增加了内容表示的复杂性,并且没有提供终端用户可以从中受益的任何功能。
有了这个架构,我们可以在添加多语言内容时确保系统的组织。
使用以下命令实现搜索Algolia.在Sanity中对文档进行更改时,可以使用webhook功能来创建,更新,或删除Algolia内容的记录。当用户搜索查询时,结果直接从Algolia提供,以最小化往返时间和延迟。Algolia记录还包含支持按用户属性(如护理协议和首选语言)分割搜索结果所需的所有相关字段。用Algolia的方面的过滤器,我们可以在搜索查询中设置用户的首选语言和护理协议,以确保我们不会显示无关的结果。这可以防止用户看到不相关的结果,并不可避免地需要不必要的人力支持。
左边的图片是我们以前的资源中心。右边的图片是我们新的发现体验,它使用了我们在整个应用程序中使用的相同的React组件。内容通过一排排的传送带呈现,我们可以无缝地补充个性化的推荐。
该图显示了我们的资源中心(绿线)的日常使用量,以及在2021年8月发现广泛推出后的新发现体验(蓝线)。当你发布一个新功能并使用推送通知吸引用户时,通常会看到一个显著的高峰,但真正的问题是,在新鲜感消失后,使用率会如何比较。正如您所看到的,在我们发布很久之后,发现功能吸引的用户是资源中心的两倍。对于一个优先考虑用户粘性的团队来说,这是一个数量上的成功!
在我在Virta的头两年里,我看到我们的病人数量增长了5倍多。随着我们继续呈指数级增长,我期待着提供更吸引人的体验,引导我们的用户走向更好的健康。我很兴奋地看到我们治疗的未来,包括人工智能驱动的内容推荐和个性化学习模块等功能。