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.

Apresentação e navegação

Apresentação cobre UI, navegação, composição de telas, estado visual, componentes reutilizáveis, acessibilidade, comportamento adaptativo, deep links e routing. Deve expressar intenção do usuário sem absorver regras de negócio.

  • Não transformar widgets ou views em entidades de domínio.
  • Tornar visíveis decisões de navegação em autenticação, onboarding e permissões.
  • Tratar acessibilidade e adaptação como comportamento de produto.

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.

Precisa aplicar arquitetura móvel a uma app real?

Use este hub para revisar decisões. Use o serviço quando o próximo passo é escopo, arquitetura, implementação ou preparação de release.

Ver serviço móvel