通过前端后端为iOS android和Web体验提供动力

Hi there 👋 I’m Skye, and I’m a Software Engineer at iZettle. I work in the Understand team in the Food & Drink Area, on our mobile apps, websites, and backend services.

嗨,我是Skye,我是iZettle的软件工程师。 我在“餐饮”区的“了解”团队中工作,在我们的移动应用程序,网站和后端服务上。

When building our new iZettle Food & Drink products, we encountered challenges in getting data to our clients in a fast and efficient way, and this article discusses how we fixed some of those issues.

在构建新的iZettle Food and Drink产品时,我们遇到了以快速有效的方式将数据提供给客户的挑战,本文讨论了如何解决其中的一些问题。

Our focus is to help our sellers understand their food and drink business, by analysing their data, and getting that analysis to them quickly so they can make the right decisions and achieve their business goals.

我们的重点是通过分析他们的数据并快速将分析结果提供给卖家,帮助他们了解他们的餐饮业务,以便他们做出正确的决定并实现他们的业务目标。

向我们的客户获取数据 (Getting data to our clients)

We’re building new web, iOS and Android based reporting experiences, which have bespoke designs to suit their respective platforms, to help us achieve our focus described above.

我们正在构建新的基于Web,iOS和Android的报告体验,这些体验已量身定制,以适合各自的平台,以帮助我们实现上述重点。

All of our seller reporting data is stored in a brand new “data warehouse” service. This service is populated with data through an event-driven system (specifically, SQS) from an upstream “source of truth” service we call “transactions”, as our sellers make sales through the iZettle Food & Drink POS (an iPad point of sale system).

我们所有的卖方报告数据都存储在全新的“数据仓库”服务中。 由于我们的卖家通过iZettle Food&Drink POS(iPad的销售点)进行销售,因此该服务通过事件驱动系统(特别是SQS)通过上游的“事实来源”服务(我们称为“交易”)填充了数据。系统)。

We started building the web experience first, alongside the data warehouse. At first, things were fine, but as the service grew and we started to build out our iOS and Android experiences, we started to hit some problems relating to what’s included in each of the service endpoints, and how they evolve. The mobile apps would end up receiving data that they don’t need, which is problematic when sellers are frequently on mobile internet.

我们首先与数据仓库一起开始构建Web体验。 最初,一切都很好,但是随着服务的增长,我们开始构建我们的iOS和Android体验,我们开始遇到一些与每个服务端点中包含的内容以及它们如何发展有关的问题。 移动应用最终将接收到不需要的数据,这在卖家频繁使用移动互联网时会出现问题。

Further, the release cadence of the website and our mobiles apps are necessarily different per platform. The website can be delivered quickly, with changes taking only a few minutes to go out to production, but our mobile apps have a two week release train for new code, because of how app stores work. Deployed versions of mobile apps must also be supported for longer, causing frustration when we’d like to change or deprecate an endpoint that was previously only used by the web.

此外,网站和我们的手机应用程序的发布节奏在每个平台上必然有所不同。 该网站可以快速交付,更改只需几分钟即可投入生产,但是由于应用程序商店的工作方式,我们的移动应用程序需要两周的新代码发布培训。 还必须更长久地支持移动应用程序的部署版本,当我们想要更改或弃用以前仅由网络使用的端点时,这会造成挫败感。

Ultimately, with our data warehouse, we want to strive for two things:

最终,我们希望通过数据仓库努力做到两件事:

  1. The time from selling something, to the data appearing correctly in a seller’s reports, should be as low as we can reasonably make it with a complicated distributed system

    从出售某物到正确显示在卖方报告中的数据的时间应尽可能短,因为我们可以使用复杂的分布式系统合理地使其
  2. Sellers should be able to request data over time periods that are helpful to them, including over the previous year, and have it appear on their chosen platform as quickly as we can reasonably get it to them

    卖方应该能够在对他们有帮助的时间段内(包括上一年)请求数据,并尽快将其显示在他们选择的平台上

We knew that, unless we changed something about how we get data to our clients, our problems would only get bigger as we added more endpoints, features, and clients.

我们知道,除非我们改变有关如何向客户获取数据的方式,否则,随着我们添加更多的端点,功能和客户,我们的问题只会越来越大。

寻求解决方案 (Working towards a solution)

As with all the problems we have to solve, we did some investigation and came up with a few possible solutions:

与必须解决的所有问题一样,我们进行了一些调查并提出了一些可能的解决方案:

  1. Have frontend developers own the presentation of data within the existing data warehouse service, requiring them to learn Go

    让前端开发人员拥有现有数据仓库服务中的数据表示形式,要求他们学习Go
  2. Investigate and implement a brand new GraphQL service

    研究并实施全新的GraphQL服务
  3. Investigate and implement a “Backend for Frontend” service, potentially in a language more suited to frontend skills

    调查并实施“后端前端”服务,可能使用更适合前端技能的语言

The team has a lot of micro-service and REST-ful service experience. The cost, for our team, of setting up new services, is relatively small.

该团队具有大量的微服务和REST风格的服务经验。 对于我们的团队而言,建立新服务的成本相对较小。

Most of the iZettle Food & Drink backend is written in Go. It’s a good language, with great performance characteristics (especially when dealing with large amounts of data), but it’s difficult for frontend team members to learn, contribute to, and context switch between when delivering their other work, which is written largely in Typescript, Kotlin, and Swift.

大多数iZettle餐饮后端都是用Go编写的。 这是一种很好的语言,具有出色的性能特征(尤其是处理大量数据时),但是前端团队成员在交付其他作品时很难学习,做出贡献并在上下文之间进行切换,这主要是用Typescript编写的,Kotlin和斯威夫特。

Other sections of the business use Kotlin to build their backend services already, and I personally have a lot of Kotlin experience, so we felt like that was a good bet.

业务的其他部门已经在使用Kotlin构建后端服务,而且我个人有很多Kotlin经验,因此我们认为这是一个不错的选择。

Our team has little experience with GraphQL and less appetite to own and maintain a brand new paradigm in our area for getting data to clients, when a more typical REST service will get the job done and let us ship quicker. It did look promising, and it is used elsewhere in the business, but we decided it wasn’t the right fit for us at the time.

我们的团队对GraphQL经验很少,并且对拥有和维护在向客户获取数据的全新范式方面的胃口不大,这时,更典型的REST服务将完成工作并让我们更快地发货。 它看起来确实很有前途,并且已在业务的其他地方使用,但是我们认为当时不适合我们。

Given all the above, we decided to try making a “Backend for Frontend” service, written in Kotlin, to focus on getting data quickly and efficiently to our clients with great native experiences. It would be owned and maintained by the frontend team, with help from backend team members to make sure everything was up to standard. 💪

考虑到以上所有因素,我们决定尝试提供一种用Kotlin编写的“后端为前端”服务,以专注于通过丰富的本机体验快速有效地为客户获取数据。 它将由前端团队拥有和维护,并在后端团队成员的帮助下确保一切都符合标准。 💪

建立BFF报告服务 (Building the BFF reporting service)

We picked Ktor as a service framework because I had some experience from other projects, and it can be as lightweight as you want it to be.

我们选择Ktor作为服务框架是因为我从其他项目中获得了一些经验,并且它可以像您希望的那样轻巧。

To make spinning up this new kind of service nice and simple, and inspired by my colleagues’ work on a Go-based service chassis internally called izettlefx, I put together a Ktor-based service chassis, exemplified by one of the tests:

为了使这种新型的服务好用且简单,并受同事在内部称为izettlefx的基于Go的服务机架上的工作启发,我整理了一个基于Ktor的服务机架,并通过以下测试之一进行了说明:

@Test fun `test liveness`() {
    val sut = ApplicationBuilder()
        .withLivenessCheck()
        .build()


    withTestApplication(sut) {
        val result = handleRequest(HttpMethod.Get, "/private/liveness")


        assertEquals(HttpStatusCode.OK, result.response.status())
    }
}

I’m really impressed with how easy Ktor makes unit testing services. With a small amount of glue code, we have separation between our “request contexts” and “response handlers”, meaning we can thoroughly test our code at multiple levels. withTestApplication in Ktor (shown above) lowers the cost of doing a more integration-style test enough that we have many of them for the service chassis, and we can be much more confident that the integration of the features is working as intended.

Ktor如何轻松进行单元测试服务给我留下了深刻的印象。 使用少量的粘合代码,我们就可以将“请求上下文”和“响应处理程序”分开,这意味着我们可以在多个级别上彻底测试我们的代码。 withTestApplication中的withTestApplication(如上所示)降低了进行更多集成式测试的成本,以至于我们有许多用于服务机箱的测试,并且我们可以更加确信功能的集成按预期进行。

In the BFF reporting service itself, request handlers often aggregate data from upstream services— combining seller information like their business details, and seller data from several different endpoints exposed by the data warehouse.

在BFF报告服务本身中,请求处理程序通常会汇总来自上游服务的数据-结合卖方信息(例如其业务详细信息)和来自数据仓库公开的多个不同端点的卖方数据。

We built the service in a way which frontend team members are comfortable with. We use ReactiveX a lot, so it made sense for us to use this tech for wrangling upstream requests to make our mobile landing page. Relatively complex upstream request combinations become simple:

我们以前端团队成员感到满意的方式构建了服务。 我们经常使用ReactiveX,因此使用这项技术处理上游请求以制作移动目标网页是很有意义的。 相对复杂的上游请求组合变得简单:

override suspend fun handle(context: RequestContext, responder: Responding) {
    val dateFrom = context.queryParameters.extractDateTime(RoutingConstants.DATE_FROM)
    val dateTo = context.queryParameters.extractDateTime(RoutingConstants.DATE_TO)
    val resolution = context.queryParameters[RoutingConstants.RESOLUTION]
    val siteId = context.pathParameters[RoutingConstants.SITE_ID]


    // ...


    val totalsResult = salesTotalsRequester.request(context, siteId, dateFrom, dateTo)
    val graphResult = salesGraphRequester.request(context, siteId, resolution, dateFrom, dateTo)
    val productsResult = productsRequester.request(context, siteId, dateFrom, dateTo, pageSize = 3)


    val outcome = Singles
        .zip(
            totalsResult.subscribeOn(scheduler).mapToOutcome(),
            graphResult.subscribeOn(scheduler).mapToOutcome(),
            productsResult.subscribeOn(scheduler).mapToOutcome()
        )
        .map(this::handleResponses)
        .await()
        
    // ...
}

Finally, requesters built to fetch upstream data from services can be reused for different endpoints and platforms — the requester that provides payment type information to our web-based Sales Report, in component-form, is reused to provide the same information in the mobile sales reports.

最后,为从服务中获取上游数据而构建的请求者可以在不同的端点和平台上重复使用-以组件形式向基于Web的销售报告提供付款类型信息的请求者可以在移动销售中重复使用,以提供相同的信息报告。

结论 (Conclusion)

Ktor has been great to work with. It has a really solid set of building blocks with which we built a small service framework. The new service is very much focused on solving a particular set of problems, without being a kitchen sink. When we need another piece, we add to the service framework incrementally.

与Ktor合作非常好。 它具有一组非常可靠的构建基块,我们使用它们构建了一个小型服务框架。 这项新服务非常着重于解决特定问题,而无需成为厨房水槽。 当我们需要另一个组件时,我们将逐步添加到服务框架中。

Trust within the team is really important — everybody took the time to listen and discuss solutions when we started to have problems building new experiences with the existing data warehouse service. Folks helped throughout with backend best practices, and the service is deployed, monitored, and managed just like all our other services, with frontend and backend team members contributing to its ongoing development.

团队内部的信任非常重要-当我们开始在使用现有数据仓库服务构建新体验时遇到问题时,每个人都花时间聆听和讨论解决方案。 人们通过后端最佳实践为整个过程提供了帮助,该服务的部署,监控和管理与我们的所有其他服务一样,前端和后端团队成员也为该项目的持续发展做出了贡献。

Our data warehouse service stays lean, and the team builds fast endpoints for extracting seller data as we want to. The BFF service worries about the aggregation and presentation, and clients get the data they need in a format that suits the platform. 🚀

我们的数据仓库服务保持精简,并且团队根据需要构建快速的端点来提取卖方数据。 BFF服务担心聚合和表示,并且客户端以适合平台的格式获取所需的数据。 🚀

翻译自: https://medium.com/izettle-engineering/powering-ios-android-and-web-experiences-with-a-backend-for-frontend-e198d55a21cc

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值