Desenvolvimento

5 abr, 2019

Construindo um rastreador de ganhos em tempo real no novo aplicativo de motorista da Uber

100 visualizações
Publicidade

Este artigo é o nono de uma série sobre como a equipe de engenharia móvel da Uber desenvolveu a versão mais recente do nosso aplicativo de motorista, codinome Carbon, um componente essencial de nosso negócio de compartilhamento de viagens.

Entre outros novos recursos, o aplicativo permite que nossa população de mais de três milhões de motoristas parceiros encontre tarifas, obtenha direções e acompanhe seus ganhos.

Começamos a projetar o novo aplicativo em conjunto com o feedback de nossos motoristas parceiros em 2017 e começamos a implementá-lo para produção em setembro de 2018.

Nosso aplicativo de motorista serve como a ferramenta de comunicação diária mais importante entre a Uber e nossos motoristas parceiros.

O aplicativo mostra os motoristas onde eles podem buscar o próximo passageiro, a rota mais eficiente para levar os passageiros aonde eles querem ir e quanto dinheiro eles ganham para cada viagem.

Entre muitos outros novos recursos, a versão mais recente do nosso aplicativo de motorista melhora muito esse último ponto no que chamamos de Rastreador de Ganhos em Tempo Real, uma forma de mostrar aos motoristas o quanto eles ganharam por viagem, dia e semana, entre outras funções.

Contando com o feedback direto dos nossos motoristas parceiros, projetamos esse recurso para mostrar não apenas quanto dinheiro eles acumulam para cada sessão de direção, mas também para dar uma visão completa de seus ganhos na plataforma.

A interface do usuário do Rastreador de Ganhos em Tempo Real inclui uma série de cartões, elementos que cobrem o aplicativo, mostrando itens como os últimos resultados da viagem e ganhos diários.

Os motoristas podem escolher entre três modos diferentes, mostrados na Figura 1, abaixo, que permitem que eles vejam seus ganhos em tempo real, revisem resumos de viagens anteriores e sessões de direção e celebrem momentos importantes na plataforma.

Figura 1: A interface do usuário do Rastreador de Ganhos em Tempo Real é fornecida com três modos que permitem que os motoristas visualizem os ganhos atuais de viagem, os resumos dos ganhos da viagem e os resultados de incentivo.

Por meio de nosso processo de design inclusivo, inicialmente lançamos o aplicativo como um produto viável mínimo (MVP), usando essa versão para coletar feedback dos motoristas parceiros.

Ao longo desta fase, adicionamos recursos solicitados ao Rastreador de Ganhos em Tempo Real, incluindo o Modo de Privacidade, que permite que os motoristas ocultem seus ganhos em tempo real na tela inicial do aplicativo, melhor aproveitamento e tratamento de erros, como mostra a Figura 2, abaixo.

O sucesso do Rastreador de Ganhos em Tempo Real pode ser medido pelo fato de que 86% dos motoristas o usam nos dias em que estão pegando passageiros e o aumento significativo de tráfego que temos visto no resumo diário.

Figura 2: Novos recursos e melhorias de engenharia levaram a um maior uso e satisfação com o Rastreador de Ganhos em Tempo Real.

Embora a maioria das informações exibidas no Rastreador de Ganhos em Tempo Real apareça automaticamente com base na atividade do motorista, também trabalhamos com outras equipes da Uber, como as equipes de Incentivos e Novas Garantias do Motorista, para permitir maior interação e suporte no aplicativo para nossos motoristas parceiros.

Para essas equipes, coletamos o feedback deles e formalizamos o guia de integração de produtos, design e engenharia para adicionar novos cartões.

O desafio de um sistema de ganhos ao vivo

Mostrar ganhos em tempo real em nosso aplicativo de motorista requer interação com vários serviços de back-end, incluindo solicitação de viagem, conclusão de viagem, armazenamento de viagem e processamento de ganhos.

Embora reformulássemos a interface do usuário, ainda precisávamos incluir vários pontos de contato no aplicativo, como um último cartão de viagem, um cartão de viagem diário, histórico de viagens e uma página inicial de ganhos.

Antes de criar o Rastreador de Ganhos em Tempo Real, identificamos três possíveis desafios para exibição de ganhos em tempo real na versão anterior de nosso aplicativo de motorista: latência e confiabilidade dos dados, consistência e confiança dos dados e correlação de produtos, descritos abaixo:

  • Latência de dados e confiabilidade: O aplicativo de motorista anterior mostrava ganhos em tempo real em cartões na tela do mapa inicial. Projetado para atualizar imediatamente após cada viagem, o aplicativo precisava extrair dados em tempo real do back-end. Esses frequentes puxões introduziram problemas de carregamento do servidor que, às vezes, causavam interrupções no sistema de exibição de ganhos.
  • Consistência de dados: Os dados de nossa exibição de ganhos vieram de várias fontes e serviços diferentes, que não foram atualizados ao mesmo tempo. Um ponto de contato no aplicativo pode mostrar dados obsoletos, enquanto outro mostrava dados mais recentes, levando a uma falta de consistência no que está sendo comunicado aos motoristas parceiros. A fim de proporcionar uma melhor experiência ao usuário para os motoristas parceiros, precisamos que essas saídas sejam alinhadas no aplicativo.
  • Correlação entre produtos: Nossa pesquisa com usuários mostrou que os motoristas parceiros consideram os ganhos e incentivos como altamente relacionados.

No entanto, o aplicativo anterior isolou essas duas exibições, mostrando ganhos em cartões e incentivos como pequenos botões na parte superior direita da tela, conforme mostrado na Figura 3, abaixo:

Figura 3: Nosso aplicativo de motorista anterior separava ganhos e incentivos em sua interface do usuário. À esquerda, a interface do usuário do aplicativo de motorista anterior mostra ganhos exibidos em cartões e incentivos na parte superior direita da tela. À direita, nossa nova interface do usuário mostra incentivos e ganhos no canto superior direito.

O framework do rastreador de ganhos em tempo real

Levando em consideração a nossa experiência anterior de criação de exibição de ganhos na versão anterior da aplicação do controlador, e testemunhando o valor da plataforma de monitorização em tempo real para os motoristas parceiros, estabelecemos os seguintes objetivos ao criar o framework por trás do Rastreador de Ganhos em Tempo Real:

  • Fazer push/envio de dados, ao contrário de pull/puxar de dados, um cidadão de primeira classe, reduzindo as solicitações do servidor.
  • Torne a consistência de dados um cidadão de primeira classe.
  • Projetar uma arquitetura escalonável e extensível para suportar novas integrações de serviços e cartões.

Para ajudar a atingir essas metas de projeto, criamos o framework de rastreamento em tempo real em cima de algumas plataformas, infraestruturas e arquiteturas existentes da Uber.

Interface com o back-end

O gateway da API em tempo real existente atua como uma interface entre nossos aplicativos e serviços de back-end. Utilizamos esse serviço como a única fonte de verdade para os modelos de dados de cartões rastreadores que precisam se integrar ao framework por meio do pipeline push da Uber ou solicitações de pull HTTP, conforme mostrado na Figura 4, abaixo.

Figura 4: O Gateway da API em Tempo Real serve como uma interface entre os aplicativos da Uber e nossos serviços de back-end. Consequentemente, usamos isso como a fonte da verdade para o Rastreador de Ganhos em Tempo Real.

O uso do Gateway da API em Tempo Real como fonte de verdade facilita muito as novas integrações de back-end no framework.

A seguir, um trecho de modelos de dados e suas explicações:

enum CardType {
   BROWSE = 1,
   BULLETIN = 2,
   BROWSE_AND_BULLETIN = 3,
   UNKNOWN = 4
} (rtapi.mobile.unknownCaseFallback = “UNKNOWN“)

struct TrackerCard {
 1: required string cardID,
 2: required double priority,
 3: required bool isValid,
 4: optional TrackerCardPayload payload,
 5: optional CardType cardType,
 6: optional ts.TimestampInSec expiresAt,
 7: optional ts.TimestampInSec lastUpdatedAt,
 8: optional OutageState outageState,
 9: optional bool shouldForceSwitchStatusMode,
 10: optional double statusModePriority
}
  • // cardID: a única fonte de verdade para identificar um cartão no framework do Rastreador, cada vez que um novo cartão rastreador é introduzido, um novo cardID é necessário
  • // priority: para classificação em dispositivos móveis e exibição de Cartões de Navegação de Rastreador de Ganhos em Tempo Real, cada vez que o aplicativo recebe uma resposta do servidor, o sistema de classificação móvel executa a classificação e atualiza a ordem de exibição dos cartões de Navegação do Rastreador de Ganhos em Tempo Real
  • // payload: os dados para preencher a interface de usuário do aplicativo devem existir
  • // cardType: diferencia os Cartões de Navegação do Rastreador de Ganhos em Tempo Real e os Cartões de Boletins do Rastreador de Ganhos em Tempo Real que são exibidos em diferentes modos do Rastreador de Ganhos em Tempo Real
  • // expiresAt: quando um Cartão do Rastreador de Ganhos em Tempo Real precisa ser invalidado, o serviço de back-end precisa definir esse campo para invalidar um cartão específico. O serviço de back-end não deve enviar push para expirar os cartões, pois pode haver problemas de carregamento se você receber muitas chamadas no mesmo horário expirado.
  • // lastUpdatedAt: cada serviço precisa definir esse campo ao preencher os dados e é exibido quando o serviço de backend está em status de interrupção enquanto os dados do cartão são armazenados em cache no lado do dispositivo móvel

Serviços integrados de back-end

Para garantir que nossos modelos de dados sejam os mais precisos possíveis, cada serviço de back-end integrado a esse framework precisa importar os modelos de dados do Cartão do Rastreador de Ganhos em Tempo Real da biblioteca de API em Tempo Real.

Recomendamos também que outras equipes que introduzem recursos se integrem ao pipeline de envio da Uber para atualizações em tempo real.

Para tornar a adição de novos cartões no aplicativo do motorista escalável e extensível, a integração de um novo cartão requer a adição de novos campos no TrackerCardPayload, o esquema de carga de dados para o framework, como mostrado no snippet de código abaixo e o design de modelos específicos de dados de payload/carga útil (por exemplo, TrackerRecentTripsCard) para preencher dados na interface do usuário do aplicativo do motorista:

TrackerCardPayload {
optional TrackerRecentTripsCard trackerRecentTripsCard
optional TrackerDailyEarningsCard trackerDailyEarningsCard
optional TrackerWeeklyEarningsCard trackerWeeklyEarningsCard
      optional TrackerDxGyProgressCard trackerDxGyProgressCard
      optional TrackerDxGyCompletionCard trackerDxGyCompletionCard
// Futuros tipos de cartão serão incluídos aqui
}

TrackerRecentTripsCard {
required string title
optional string formattedTotal
optional string formattedRequestAt
optional string vehicleStatusDescription
optional string callToAction
optional string bulletinTitle
optional string lastTripUuid
}

Na maioria dos casos, a carga útil deve vir do back-end e ser consumida diretamente pela interface do usuário móvel.

Construindo o rastreador de ganhos em tempo real com RIBs

Criamos o novo aplicativo de motorista usando RIBs, a arquitetura móvel de plataforma aberta de código aberto da Uber. Essa arquitetura se mostrou útil para as complexas transições necessárias para os três modos diferentes do Rastreador de Ganhos em Tempo Real: Status, Navegador e Boletim. A arquitetura de RIBs permite que o Rastreador de Ganhos em Tempo Real seja escalonável, para que possamos adicionar mais cartões de exibição no futuro.

A árvore RIBs do Rastreador de Ganhos em Tempo Real inclui quatro RIBs e dois plugins, descritos abaixo:

  • RIB TrackerEntry: este RIB representa o modo de Status no Rastreador de Ganhos em Tempo Real e é o RIB filho de Active. Quando tocado por um motorista parceiro ou ao receber um gatilho no modo de boletim, o RIB do Rastreador será anexado como um filho de tela inteira do TrackerEntry.
  • RIB do Rastreador: esse RIB serve como um contêiner para RIBs do BrowseTracker/Rastreador do Navegador e do BulletinTracker/Rastreador de Boletim.
  • RIB do BrowseTracker: representando o modo de Navegação do Rastreador de Ganhos em Tempo Real, esse RIB é um contêiner para os cartões de Navegação, como Resumo Diário, Resumo Semanal e Última Viagem, na interface do usuário do aplicativo.
  • RIB BulletinTracker: representando o modo Boletim do Rastreador de Ganhos em Tempo Real, este RIB exibe cartões de Boletim no aplicativo, mostrando coisas como quando a última viagem foi processada e vários marcos para o motorista. Ele lida com eventos push que acionam boletins especiais.
  • BrowseCardPlugin e BulletinCardPlugin: qualquer cartão exibido no Rastreador de Ganhos em Tempo Real é um plugin para que o cartão possa ser desativado sem afetar as funções principais. Especificamente, a integração no modo de Navegação faz a ponte entre os cartões de Navegação baseados em plug-in, como o cartão de resumo semanal e o cartão de resumo diário, na funcionalidade principal. Cartões de boletins, como First Trip/Primeira Viagem e marcos de viagens do uberPOOL desbloqueadas, fazem pontes com cartões de Boletim baseados em plug-ins na funcionalidade principal para integração no modo Boletim.
Figura 5: A árvore RIBs do Rastreador de Ganhos em Tempo Real inclui quatro RIBs e dois plugins.

Lógica de negócios principal de front-end

A principal lógica de negócios no frontend acontece em um componente chamado TrackerDataManager. O componente coleta fluxos de dados, como ganhos e incentivos, de diferentes origens de upstream, manipula os dados usando operações como agregação, classificação e validação e, em seguida, os envia para os diferentes fluxos do modo Rastreador de Ganhos em Tempo Real de acordo com a designação do cardType, conforme mostrado na Figura 6, abaixo:

Figura 6: O TrackerDataManager lida com a principal lógica de negócios no lado do aplicativo, processando fluxos de dados de entrada e preenchendo dados para os diferentes modos do Rastreador de Ganhos em Tempo Real.

Cada entrada de fluxo de dados para TrackerDataManager contém uma lista de cartões do Rastreador de Ganhos em Tempo Real.

Para garantir a consistência dos dados em todo o aplicativo, cada serviço de back-end integrado precisa ter seu próprio gerenciador de dados para gerenciar seus cartões Rastreador de Ganhos em Tempo Real.

Por exemplo, o RealtimeEarningsManager gerencia cartões relacionados a ganhos, incluindo Última Viagem, Resumo Diário e Resumo Semanal. O RealtimeIncentiveManager lida com cartões relacionados a incentivos, incluindo o Rastreamento de Busca.

Cada gerenciador de dados de fluxo precisa lidar com as lógicas pull e push e ser autônomo para garantir os dados consistentes e atualizados.

Observando os ganhos como exemplo, o RealtimeEarningsManager será criado como um singleton no escopo ativo e passará o real-timeEarningsStream como uma dependência para o TrackerDataManager.

Aderindo a um conceito de isolamento de escopo, queremos apenas passar os dados mínimos para o escopo correto.

Para fazer isso, separamos as saídas do TrackerDataManager em três fluxos de dados que representam os três modos do Rastreador de Ganhos em Tempo Real. Cada fluxo será passado para os consumidores do RIB relacionados para preencher os dados:

  • TrackerStatusModeStream
  • TrackerBrowseModeStream
  • TrackerBulletinModeStream
/// @CreateMock
public protocol TrackerStatusModeListener: class {
   func update(selectedItem: BrowseCardContext)
}

/// @CreateMock
public protocol TrackerStatusModeStream: class {
   var selectedItem: Observable<BrowseTrackerRankingItem> { get }
   var hasSortedValidItems: Bool { get }
   var getBulletinCardPush: Observable<TrackerCard> { get }
   var privacyStatus: Observable<TrackerPrivacyStatus> { get }
}

/// @CreateMock
public protocol TrackerBrowseModeStream: class {
   var dailyDetails: Observable<[EarningsDetails]> { get }
   var earningsError: Observable<Error?> { get }
   var earningsTrackerCardsWrapper: Observable<[EarningsModelWrapper<TrackerCard>]> { get }
   var incentivesErrorByType: Observable<[IncentiveCardID: Error]> { get }
   var privacyStatus: Observable<TrackerPrivacyStatus> { get }
   var sortedValidItems: [BrowseTrackerRankingItem] { get }
   var trackerCards: Observable<[TrackerCard]> { get }

   func update(privacyStatus: TrackerPrivacyStatus)
}

/// @CreateMock
public protocol TrackerBulletinModeStream: class {
   var bulletinCard: TrackerCard? { get }
}

Melhoria da plataforma

Depois de integrar incentivos em nosso Rastreador de Ganhos em Tempo Real, coletamos feedback útil sobre a implementação existente da equipe de Incentivos, como solicitações de melhor integridade da integração e uma API mais limpa.

Conseguimos melhorar ainda mais a eficiência da integração e melhorar a plataforma para permitir uma integração mais rápida e segura de novos produtos e recursos:

  • Use o tipo enum para garantir segurança em tempo de compilação ao adicionar novos cartões, garantindo alta integridade de integração.
  • Melhore o isolamento do código e o design da API para que a adição de um novo cartão não afete a funcionalidade principal.
  • Forneça modelos de interface do usuário básicos e RIBs de cartão base, removendo completamente a interface do usuário e o código da lógica de negócios, para melhorar a produtividade do desenvolvedor.

O futuro do rastreador de ganhos em tempo real

O Rastreador de Ganhos em Tempo Real foi adotado por equipes em toda a nossa plataforma para aumentar a transparência dos ganhos e o engajamento do motorista parceiro. No segundo semestre de 2018, 24 recursos foram integrados ao Rastreador de Ganhos em Tempo Real em seus três modos diferentes:

  • Modo de status: última viagem, resumo diário, progresso de viagens consecutivas, progresso da Busca, progresso das garantias do novo motorista, progresso de lealdade, erro de ganhos, erro de incentivo, erro de progresso das garantias do novo motorista, erro de progresso de lealdade.
  • Modo de navegação: última viagem, resumo diário, progresso consecutivo das viagens, progresso da busca, progresso de garantias do novo motorista, progresso de fidelidade, erro de ganhos, erro de incentivo, erro de progresso das garantias do novo motorista e erro de progresso de lealdade.
  • Modo de boletim: conclusão da última viagem, conclusão de viagens consecutivas, conclusão da busca, conclusão das garantias do novo motorista.

Mais recentemente, integramos o programa Uber Pro no Rastreador de Ganhos em Tempo Real para ajudar a celebrar as realizações de nossos motoristas parceiros, conforme mostrado na Figura 7, abaixo.

Figura 7: Estes cartões mostram alguns dos recursos adicionados aos diferentes modos do Rastreador de Ganhos em Tempo Real.

Em 2019, planejamos introduzir ainda mais novos recursos no Rastreador de Ganhos em Tempo Real para melhorar ainda mais a experiência do motorista parceiro em nossa plataforma.

Índice dos artigos na série do aplicativo de motorista da Uber:

Interessado em desenvolver aplicativos móveis usados ​​por milhões de pessoas todos os dias? Considere se juntar à nossa equipe como engenheiro Android ou iOS!

***

Este artigo é do Uber Engineering. Ele foi escrito por Zebing Zong. A tradução foi feita pela Redação iMasters com autorização. Você pode conferir o original em: https://eng.uber.com/real-time-earnings-tracker/