Arquitetura de aplicações móveis
Arquitetura móvel é muito mais do que telas e gerenciamento de estado.
Um mapa prático para camadas móveis, propriedade de estado, regras de domínio, dados, APIs, autenticação, offline, capacidades nativas, testes, releases e evolução.Definição
Arquitetura de aplicações móveis é o conjunto de decisões estruturais que organiza apresentação, estado, regras de negócio, dados, integrações e capacidades da plataforma para que uma aplicação possa evoluir com segurança. Não significa adicionar muitas camadas por padrão. Significa escolher limites adequados para usuários, fluxos, backend, dispositivos e vida útil do produto.
Por que arquitetura móvel importa
Uma app móvel vive sob restrições próprias: conectividade variável, dispositivos diferentes, ciclo de vida do sistema operacional, permissões, armazenamento local, bateria, desempenho, atualizações distribuídas, compatibilidade com backend e versões antigas instaladas. A arquitetura deve tornar essas restrições visíveis sem transformar entrega em burocracia.
Problemas que ajuda a esclarecer
- Telas passam a misturar regras de negócio, mapeamento de APIs e tratamento de erros.
- Gerenciamento de estado é tratado como toda a arquitetura em vez de uma decisão dentro dela.
- Modelos de transporte das APIs vazam para conceitos de domínio.
- Autenticação, autorização e permissões do dispositivo viram uma preocupação ambígua.
- Comportamento offline é assumido sem fonte de verdade, retries ou conflitos definidos.
- Capacidades nativas ficam acopladas à UI e difíceis de testar ou substituir.
- Releases móveis são planejados como deploys web, embora rollback e revisão de loja sejam diferentes.
- Testes focam telas enquanto regras e fluxos críticos ficam descobertos.
Contexto e restrições
A arquitetura começa pelas restrições: usuários, fluxos, conectividade, sensibilidade dos dados, tamanho do time, velocidade, plataformas alvo, backend, integrações, capacidades nativas, estratégia de release e vida útil. Um padrão escolhido antes desse contexto costuma ser decoração.
- Uma app experimental pequena pode ser simples de propósito.
- Uma app de fluxos de negócio precisa de limites mais claros em domínio, dados e integração.
- Um canal móvel de uma plataforma SaaS deve coordenar identidade, permissões e releases com o backend.
Gerenciamento de estado
Gerenciamento de estado é parte da arquitetura móvel, não toda a arquitetura. A pergunta útil é propriedade: estado local de UI, tela, aplicação, domínio, servidor e persistido têm ciclos de vida e falhas diferentes.
- Definir quem possui loading, erros, invalidação, estado derivado e sincronização.
- Escolher Provider, Bloc ou Riverpod como ferramentas, não regras universais.
- Evitar containers de estado para esconder limites de domínio pouco claros.
Domínio e regras de negócio
Lógica de domínio inclui casos de uso, workflows, validação, invariantes e regras que não deveriam depender de uma tela ou framework. O nível de modelagem deve corresponder à complexidade real.
- Modelar fluxos críticos onde mudança ou erro custa caro.
- Manter validações e invariantes testáveis sem renderizar telas.
- Usar limites leves quando DDD completo seria desproporcional.
Dados e persistência
Arquitetura de dados móvel decide como dados remotos, locais, cache, persistência, serialização, migrações, invalidação, secure storage, ciclo de vida, propriedade e exclusão convivem. Nem tudo precisa de persistência local.
- Separar modelos de transporte de conceitos de domínio.
- Usar armazenamento seguro para material sensível de sessão quando apropriado.
- Planejar invalidação e exclusão antes que dados locais se acumulem.
Backend e APIs
Uma app móvel depende de contratos de API, mapeamento, retries, timeouts, paginação, rate limits, tradução de erros, autenticação, compatibilidade, versionamento, idempotência e trabalho em background. Backend e mobile não são lançados no mesmo ritmo.
- Mapear dados de transporte antes de chegar ao domínio.
- Tornar explícitos retries, timeouts e tradução de erros.
- Coordenar versionamento de APIs com versões móveis instaladas.
Autenticação e permissões
Autenticação prova identidade. Autorização decide o que essa identidade pode fazer. Permissões do dispositivo controlam capacidades da plataforma. A arquitetura deve distinguir tudo isso e lidar com tokens, expiração, acesso revogado, roles, claims, least privilege e feedback.
- Não confundir permissão do sistema operacional com permissão de negócio.
- Planejar sessões expiradas ou revogadas como fluxos normais.
- Pedir permissões perto da intenção do usuário.
Offline e sincronização
Offline não é apenas salvar dados. Envolve fonte de verdade, mudanças em fila, resolução de conflitos, retries, ordem, timestamps, idempotência, dados stale, feedback e recuperação de conectividade. Algumas apps são offline-capable, outras offline-tolerant e só algumas offline-first.
- Escolher estratégia offline pela necessidade do produto.
- Projetar conflitos antes que usuários dependam de mudanças locais.
- Não afirmar offline-first se a evidência pública só mostra fluxos conectados.
Capacidades nativas e código compartilhado
Flutter pode facilitar base compartilhada, UI consistente, componentes reutilizáveis e entrega coordenada Android/iOS. Não elimina decisões de domínio, estado, backend, dados, permissões, testes, releases ou limites nativos.
- Compartilhar código onde o comportamento do produto é realmente comum.
- Isolar integrações nativas atrás de interfaces explícitas.
- Não transformar Flutter na arquitetura completa.
Tratamento de erros e resiliência
Falhas esperadas incluem rede, validação, autenticação, backend, persistência local e permissões. A arquitetura deve decidir o que tenta novamente, o que tem fallback, o que precisa de mensagem ao usuário e o que requer diagnóstico.
- Evitar catches genéricos que escondem comportamento.
- Projetar mensagens de erro como parte da experiência.
- Manter contexto diagnóstico útil sem dados sensíveis desnecessários.
Estratégia de testes
Testes móveis funcionam melhor quando o risco decide o nível: unitários, domínio, repositórios, integração, widgets/componentes, end-to-end e validação de release respondem perguntas diferentes.
- Não testar tudo pela camada mais lenta.
- Priorizar regras, limites de integração e fluxos críticos.
- Usar testes de UI onde o risco de interação é real.
Release e entrega
Release móvel inclui variantes de build, ambientes, signing, configuração, CI/CD, canais, revisão de loja, staged rollout, compatibilidade backend, feature flags e monitoramento depois do release. Um rollback móvel não funciona como um rollback web.
- Planejar configuração e signing cedo.
- Coordenar backend com versões instaladas.
- Usar Google Play como evidência real de Bozz Apps sem afirmar App Store não verificado.
Observabilidade e diagnóstico
Observabilidade móvel deve capturar crashes, sinais de desempenho, rede, versão, ambiente, contexto seguro e correlação com backend. O objetivo é entender falhas sem registrar dados sensíveis desnecessários.
- Incluir versão e ambiente em diagnósticos.
- Conectar falhas móveis com comportamento backend quando possível.
- Manter limites de privacidade explícitos.
Evolução e dívida técnica
Apps evoluem por dependências, mudanças de OS, SDKs, políticas de loja, dispositivos antigos, design, domínio, APIs, migrações e features depreciadas. Reescrever tudo raramente é a primeira resposta.
- Detectar desvio arquitetural antes que vire comportamento de produto.
- Atualizar dependências considerando risco de release.
- Coordenar evolução móvel, backend e produto.
Quando não sobrearquitetar
Uma app pequena com fluxos simples, poucas telas, backend estável, baixa complexidade, sem offline e vida curta não precisa de cinco camadas vazias. Simplicidade ainda precisa de limites, testes onde há risco e tratamento claro de erros.
- Usar arquitetura proporcional à complexidade.
- Manter apps simples legíveis antes de adicionar abstrações.
- Adicionar limites quando mudança, risco ou colaboração justificarem.
Princípios
Manter regras fora da UI
Workflows e validações importantes devem ser testáveis sem renderizar telas nem depender de framework.
- Mover regras críticas para fora de widgets.
- Deixar a UI orquestrar, não possuir o domínio.
Tornar explícita a propriedade do estado
Estado fica gerenciável quando propriedade, ciclo de vida, invalidação e persistência são nomeados.
- Separar estado local, servidor, domínio e persistido.
- Projetar loading e erros com o mesmo cuidado que sucesso.
Tratar falhas de conectividade como normais
Usuários móveis passam por redes instáveis; caminhos de falha são parte do produto.
- Escolher offline-capable, offline-tolerant ou offline-first deliberadamente.
- Mostrar recuperação e dados stale com clareza.
Separar transporte de domínio
Payloads de API não devem ditar conceitos internos quando o comportamento precisa de estabilidade.
- Mapear DTOs nos limites.
- Manter linguagem de domínio perto de workflows, não endpoints.
Isolar integrações nativas
Capacidades da plataforma devem ficar isoladas para que UI e domínio não dependam diretamente de APIs do dispositivo.
- Encapsular permissões e platform channels.
- Testar comportamento sem hardware quando possível.
Testar onde existe risco de fluxo
O nível de teste depende do custo de falha, não de um slogan de cobertura.
- Priorizar risco de domínio e integração.
- Usar end-to-end para rotas críticas.
Coordenar mobile e backend
Versões instaladas e releases backend têm ciclos diferentes, então compatibilidade precisa de planejamento.
- Versionar contratos quando necessário.
- Planejar releases considerando clientes antigos.
Usar arquitetura proporcional
Boa arquitetura protege mudança e risco reais sem importar cerimônia de sistemas maiores.
- Começar simples se a complexidade é baixa.
- Adicionar camadas quando elas esclarecem propriedade.
Decisões arquiteturais chave
Código compartilhado ou específico por plataforma
Pergunta: que comportamento deve ser compartilhado e qual deve ser nativo? Contexto: Flutter compartilha UI e lógica, mas capacidades de plataforma podem diferir. Trade-off: reutilização versus ajuste nativo. Risco: forçar tudo para compartilhado fragiliza o nativo.
- Compartilhar comportamento realmente comum.
- Isolar integrações específicas.
Propriedade do estado
Pergunta: quem possui cada estado? UI, domínio, servidor e persistência mudam em ritmos diferentes. Trade-off: simplicidade versus ciclo de vida explícito. Risco: bugs por estado stale ou duplicado.
- Nomear donos e invalidação.
- Evitar uma única bolsa global para tudo.
Limites de domínio
Pergunta: onde vivem as regras de negócio? Trade-off: velocidade versus testabilidade. Risco: regras escondidas em telas difíceis de mudar.
- Modelar as regras que importam.
- Evitar DDD cerimonial em fluxos triviais.
Fonte de verdade remota e local
Pergunta: que dado é autoritativo? APIs, caches e persistência local podem divergir. Trade-off: resposta rápida versus consistência. Risco: usuários agem sobre dados conflitantes.
- Definir cache e sincronização.
- Explicar estados stale na UI.
Modelo de integração com APIs
Pergunta: como a app conversa com backend? Contratos, erros, retries e versionamento moldam confiabilidade. Risco: mudanças no backend quebram versões instaladas.
- Traduzir erros backend em estados de produto.
- Coordenar compatibilidade com releases.
Estratégia de autenticação e sessão
Pergunta: como lidar com identidade, autorização e sessão? Tokens expiram, acesso é revogado e permissões mudam. Risco: usuários ficam em estados inválidos.
- Separar login, roles e permissões do dispositivo.
- Tratar expiração explicitamente.
Estratégia offline
Pergunta: a app será offline-capable, offline-tolerant ou offline-first? Trade-off: resiliência versus complexidade de sync. Risco: mudanças locais entram em conflito ou desaparecem.
- Projetar retries e conflitos.
- Não adicionar offline-first sem necessidade real.
Release e observabilidade
Pergunta: como versões são publicadas, monitoradas e diagnosticadas? Store review e staged rollout diferem de web. Risco: falhas são descobertas tarde sem rollback claro.
- Rastrear versão e ambiente.
- Planejar compatibilidade com instalações existentes.
Perspectivas futuras em desenvolvimento
Estes temas estão planejados sob o hub de arquitetura móvel. Ainda não são rotas públicas.
- Gerenciamento de estado não é arquitetura móvel.
- Aplicações offline-capable versus offline-first.
- Como separar lógica de domínio da UI Flutter.
- Padrões repository sem abstração desnecessária.
- Como coordenar releases móveis e backend.
- Testes de workflows móveis no nível correto.