Neste post
O RodaGrana responde uma pergunta simples para motorista de aplicativo: quanto sobra desta corrida, depois do custo real do carro.
A resposta é aritmética sobre dados do próprio usuário. Não há nada nela que precise sair do aparelho.
Mesmo assim, o reflexo do setor seria colocar um servidor no meio: cadastro, sincronização, back-end. Este post explica por que não fizemos isso, e o que muda de concreto.
O teste que decide
Antes de escolher stack, uma pergunta:
Existe alguma funcionalidade que exija que dois aparelhos diferentes vejam o mesmo dado?
No RodaGrana: não. O motorista registra os gastos dele, roda os quilômetros dele e vê o resultado dele. Não há feed, não há ranking, não há compartilhamento.
Quando a resposta é não, servidor não é infraestrutura — é dependência adicionada de graça, com custo de latência, de disponibilidade, de privacidade e de conta em nuvem para pagar todo mês.
| Com servidor | Sem servidor |
|---|---|
| Cadastro antes do primeiro uso | abre e usa |
| Falha se a rede cair | funciona igual |
| Dados sob responsabilidade do estúdio | dados no aparelho do usuário |
| Custo mensal por usuário ativo | custo zero |
| Sincroniza entre aparelhos | não sincroniza |
A última linha é o preço. Todas as outras são o que se ganha.
Motorista trabalha em subsolo de shopping, em garagem, em estrada sem cobertura. Um app de controle financeiro que precisa de rede para somar dois números falha exatamente onde o usuário está.
Por que Drift, e não SQLite cru
SQLite é o banco. Drift é a camada em cima dele, e a escolha se justifica por duas coisas concretas.
Consulta tipada em tempo de compilação
Sem isso, consulta é string. Erro de nome de coluna aparece em execução, no aparelho do usuário, meses depois — e no pior caso não aparece: retorna vazio e o app mostra zero como se fosse um resultado.
Com Drift, o esquema é declarado em Dart, e uma consulta com coluna inexistente não compila. É o mesmo argumento de tipagem em qualquer lugar, só que o custo do erro aqui é mais alto porque o dado é do usuário e não dá para reprocessar.
Migração versionada
Todo app com banco local eventualmente precisa mudar o esquema. Sem versionamento de migração, isso vira um if que ninguém entende seis meses depois — e o usuário que estava em duas versões atrás quebra.
Migração declarada é chata de escrever e é a diferença entre atualizar e perder o histórico de alguém.
schema v1 -> v2 : adiciona coluna "custo_por_km"
schema v2 -> v3 : quebra "manutencao" em provisao e realizado
Esse arquivo é o que permite publicar atualização sem medo.
Riverpod 3 e a fronteira que ele impõe
O estado do RodaGrana vive em Riverpod, e a razão principal não é reatividade — é separação de camadas.
O padrão é este:
banco (Drift) -> repositorio -> provider -> widget
O widget não sabe que existe SQLite. O repositório não sabe que existe tela. E o provider é onde a regra de negócio mora, testável sem widget e sem banco de verdade.
O ganho prático aparece quando se quer testar a conta que importa: custo por km, lucro da corrida, fechamento do dia. Essas funções não precisam de aparelho, de banco nem de tela para serem verificadas — e são justamente as que não podem errar, porque erro nelas é o usuário tomando decisão financeira errada.
Num app cujo produto é uma conta, o teste da conta vale mais que qualquer outro. E ele só é barato se a conta não estiver misturada com a interface.
O que se perde, dito sem eufemismo
Não há sincronização. Trocou de celular, o histórico não vem junto — a menos que use exportação ou o backup do sistema.
Não há recuperação de conta. Não existe conta. Perdeu o aparelho sem backup, perdeu o dado.
Não há visão agregada. Sem servidor, não há como saber quanto o usuário médio gasta por km, e portanto não há benchmark entre motoristas — que seria um recurso legítimo.
Não há e-mail para retenção. Sem cadastro, não há como avisar de recurso novo a não ser pela loja.
As duas primeiras são mitigáveis por exportação. As duas últimas são o custo real, e são de produto, não de engenharia.
O que se ganha, e é bastante
Abre e usa. Sem tela de cadastro, sem verificação de e-mail, sem espera. A tela de cadastro é onde a maior parte dos aplicativos perde gente, e é curioso que ela seja tratada como inevitável.
Funciona sempre. Sem estado de erro de rede, sem spinner, sem "tente novamente". A lista inteira de casos de falha por conectividade simplesmente não existe.
Privacidade por construção. Não há servidor com dado financeiro de motorista. Não é promessa de política de privacidade — é o fato de o dado não sair do aparelho. A diferença entre as duas coisas é enorme e o usuário raramente consegue distinguir; aqui não precisa confiar.
Custo operacional zero. Um app sem back-end não tem conta mensal. Para estúdio pequeno isso muda quais projetos são viáveis.
Quando essa escolha deixa de valer
Três gatilhos, e vale escrevê-los antes de precisar deles:
- 1. Recurso social. Comparar, compartilhar, ranquear — qualquer coisa que
envolva dois usuários exige servidor.
- 2. Uso em mais de um aparelho como caso principal. Não como conveniência:
como o jeito normal de usar.
- 3. Dado que precisa sobreviver ao aparelho. Quando perder o celular deixa de
ser inconveniente e passa a ser inaceitável.
Nenhum deles é o caso hoje. E a ordem é favorável: com o banco local como fonte da verdade, adicionar sincronização depois é uma camada por cima, com fila de mudanças e política de conflito. Tirar o servidor depois seria reescrita.
Começar local e crescer para sincronizado é evolução. Começar em nuvem e voltar para local é o tipo de refatoração que ninguém faz.
Resumo
- A pergunta que decide: existe funcionalidade que exija dois aparelhos vendo o
mesmo dado? Se não, servidor é dependência gratuita.
- Drift entrega consulta tipada em tempo de compilação e migração versionada.
As duas evitam erro que só apareceria no aparelho do usuário.
- Separar banco, repositório, provider e widget é o que torna a conta — que é o
produto — testável sozinha.
- Perde-se sincronização, recuperação de conta, benchmark e canal de e-mail.
- Ganha-se abertura sem cadastro, funcionamento sem rede, privacidade por
construção e custo zero.
- Local para sincronizado é evolução. O caminho inverso é reescrita.
Perguntas frequentes
Por que Drift e não SQLite direto?
Porque Drift dá consulta tipada em tempo de compilação e migração versionada, duas coisas que você acaba escrevendo à mão de qualquer jeito e escreve pior. O SQLite continua embaixo — Drift é a camada que evita SQL em string e erro de coluna descoberto em produção.
Como fazer backup se não há servidor?
Exportação de arquivo pelo próprio aparelho, e backup do sistema operacional. É menos conveniente que sincronização em nuvem e é uma troca consciente: sem conta, não há o que vazar e não há o que perder por indisponibilidade de serviço.
E se um dia precisar sincronizar entre aparelhos?
Aí o banco local vira a fonte da verdade e a sincronização é uma camada por cima, com fila de mudanças e resolução de conflito. Sair de local para sincronizado é evolução; sair de servidor para local é reescrita. A ordem importa.
Isso não limita monetização?
Limita algumas formas e libera outras. Sem conta não há e-mail para retenção; por outro lado, o custo operacional é praticamente zero e o app funciona no primeiro segundo, sem cadastro — o que reduz o abandono na abertura, que é onde a maioria dos aplicativos perde gente.