Arquitetura
As vantagens não tão óbvias de trabalhar em aplicações offline-first
Como uma arquitetura offline-first pode simplificar a modelagem de dados, o compartilhamento de regras e a validação de novas ideias.
By Vinícius Amélio
Algumas aplicações, por natureza, têm um núcleo de funcionalidades que depende principalmente da interação dos usuários para gerar valor. São aplicações cuja ideia central pode ser chamada de content-driven: apps de gerenciamento de tarefas e projetos, escrita de documentos, galerias de mídia, blocos de notas e outros semelhantes.
Ainda que essas aplicações possam ser colaborativas por design, um ponto forte é que elas costumam funcionar offline. É claro que essa tolerância varia de acordo com cada produto, mas, de maneira geral, é algo comum entre elas.
Obviamente, nem todo produto digital pode funcionar dessa maneira. Em alguns casos, é necessário priorizar a consistência em tempo quase real em vez da consistência eventual das informações. Talvez esse seja, inclusive, um bom tópico para uma postagem futura.
Pense em uma aplicação que trabalha com o saldo de uma conta bancária, benefícios ou pontos de cashback. Ela não pode correr o risco de exibir um saldo desatualizado para o usuário.
Dito isso, é válido trazer um pouco da experiência que tive trabalhando no Physikos ao longo do último ano, especialmente em relação aos ganhos de produtividade no desenvolvimento e nos testes do produto que, à primeira vista, não são tão óbvios.
Esquema de dados
Aplicações que dependem do servidor como fonte da verdade tendem a exigir que, durante o desenvolvimento de uma nova feature, o backend exponha uma série de contratos de integração. No fim, esses contratos manipulam dados novos, ou até dados existentes de uma maneira nova, na fonte de dados remota.
Desenvolver o client desse produto antes de a feature estar pronta significa assumir o uso de mocks e trabalhar com base em uma promessa. Isso não chega a ser um problema quando você mesmo está fazendo tudo de ponta a ponta. Por outro lado, quando surgirem mudanças, eventualmente você poderá, e provavelmente irá, precisar refazer parte daquela entrega.
Isso não acontece da mesma forma quando a principal fonte de dados da aplicação roda localmente no dispositivo do usuário. Essa fonte local já é a fonte da verdade e, eventualmente, ficará consistente com a fonte remota.
O client passa a tomar como fonte da verdade aquilo que possui de maneira palpável e trabalha com atualizações otimistas. Afinal, a rejeição do servidor também importa, mas não suprime o estado atual.
Essas tratativas são necessárias quando você está trabalhando na concepção de algo que já existe. Mas e quando esse não é o caso? Quando ainda não existe o que sincronizar, como nos momentos iniciais do desenvolvimento de uma feature, a única fonte da verdade é o frontend.
Isso permite modelar estruturas de dados, regras de negócio e validações inicialmente voltadas ao client para, depois, levá-las ao servidor.
Se tenho uma tabela de treinos e preciso adicionar um campo de validade a esses treinos, basta incluí-lo no esquema do app e montar todo o fluxo a partir dali.
Uma vez que tudo esteja consolidado, posso portar as regras para outra camada, supondo que ainda não exista uma forma compartilhada de executá-las, com algo como WebAssembly, e também gerenciar essa persistência no backend.
Compartilhamento de regras
Se você tem uma stack fechada na mesma linguagem base, como NestJS e React Native, pode facilmente criar pacotes que compartilham interfaces, regras de validação e regras de negócio entre as duas aplicações.
Também seria possível criar uma DSL compartilhada entre backend e frontend, apesar do trabalho envolvido. Outra alternativa seria utilizar WebAssembly ou alguma linguagem que possa rodar de forma embarcada e concentrar essas regras ali.
Mesmo não sendo exatamente uma vantagem exclusiva de aplicações offline-first, a necessidade, ou melhor, a conveniência de manter esse tipo de regra centralizada acaba criando um ponto de coerência maior para a correção de bugs e para os tratamentos que se tornam necessários à medida que o projeto evolui.
Tempo de validação de POCs
Com a autonomia que um único desenvolvedor consegue ter trabalhando dessa forma, também há uma redução no tempo necessário para validar ideias, sem que o desenvolvimento no servidor precise ser feito logo no início.
Consigo sair rapidamente do ponto zero até uma feature e disponibilizá-la, já com funcionamento real, para os times de produto e desenvolvimento. A partir daí, posso iterar sobre ela até que seja de fato adotada ou descartada.
O ponto central é quanto esforço e quantas pessoas preciso mobilizar para ter algo realmente funcional em mãos, sem depender de mocks que não representam diferentes estados e comportamentos.