O Objetivo deste projeto é exemplificar algumas situações rotineiras em uma aplicação de grande porte na tentativa de encontrar as melhores soluções e padrões utilizados no mercado com o intuito de melhorar a experiência do desenvolvedor, seja ele(a) Júnior, Pleno ou Sênior.
Neste sentido e no contexto de uma aplicação flutter para dispositivos móveis, podemos identificar alguns dos principais pontos de atenção que serão exemplificados neste projeto:
Para uma melhor organização do projeto ele será segmentado em pacotes (módulos), visando o mínimo de dependencia possível entre cada módulo. A divisão se dará da seguinte forma:
Nome do pacote | Descrição |
---|---|
beleza_app | Ponto de entrada da aplicação |
core | Guarda a rota incial de todos os outros pacotes e também inicializa o Eventbus |
home | Login, Cadastro, Recuperação de senha |
dashboard | Página inicial do app |
package_manager | Gerenciamento de pacotes externos |
event_bus | Comunicação entre os módulos |
design_system | Widgets, Fontes, Cores e Ícones |
shared | Informações compartilhadas entre os pacotes (sessão por exemplo) |
profile | Informações relativas ao usuário logado |
http | Abstração de pacotes para acesso a API'S (Dio, Uno, Http, etc.) |
todos os outros | Pacotes que compoem seu app |
- data
- datasources
- models
- infrastructure
- datasources
- repositories
- domain
- entities
- errors
- usecases
- repositories
- presentation
- feature
- bloc
- feature
- Pacotes utilizados:
- bloc
- flutter_bloc
Observação:
Trabalhar com bloc de duas formas:
- Uma Classe para cada estado: Exemplo
- Uma Classe com uma propriedade Enum que denomina o estado: Exemplo
- Pacote utilizado:
- flutter_modular
A injeção de dependencias que este pacote oferece nos permite segmentar toda aplicação por pacotes, agrupando e organizando essas dependências dentro do contexto ao qual elas pertecem, facilitando a compreensão e manutenção.
- Pacote utilizado:
- flutter_modular
- Pacote utilizado:
- flutter_secure_storage
Armazenamento de dados local com segurança
- Pacote utilizado:
- firebase
- Atomic Design
- Pacote utilizado:
- dartz
Quando você pensa em erros, existem dois tipos possíveis deles:
- Erros dos quais seu software pode se recuperar:
Ex: Uma senha inválida - Erros dos quais seu software não pode se recuperar ou não faz sentido recuperar:
Ex: Conversão de um valor nulo para inteiro
Se não houver uma maneira de se recuperar de um erro a melhor forma de lidar com isso é maximizar a probabilidade de um engenheiro lidar com isso para que a solução seja tomada da forma mais rápida.
No Dart, a maneira de sinalizar um erro para o chamador é lançando um Exception
ou Error
.
Agora observe a seguinte função:
Uri getUri(String url){
if(url.isEmpty){
throw ArgumentError('url não pode ser vazio!')
}
return Uri.parse(url);
}
Como o chamador saberá que precisa lidar com erros lançados por sua função?
A função acima deverá converter uma URL em uma URI, porém caso a string passada não estiver no formato esperado, uma exeção será disparada. Esta função tem um erro implícito.
Quando o erro é imlplícito, o desenvolvedor precisa consultar a documentação ou os comentários da função para verificar se a mesma pode retornar alguma exceção e se previnir quanto a isso. Nesse caso, é um risco de que o tratamento correto não seja realmente feito ocasionando um mal funcionamento do sistema.
- Se você puder se recuperar do erro, isso significa sinalizar para o chamador imediato (um ou dois níveis mais alto na cadeia) para que ele possa lidar com isso normalmente.
- Se você não conseguir se recuperar do erro, em algum lugar central do seu programa, você registra o erro e aborta. Por exemplo, no Crashlytics do Firebase.
Existe uma forma de transformar erros implícitos em explícitos através do tipo de retorno Either
.
Ao seguir esse padrão, você garante que o usuário do seu método nunca perca o cenário de erro. Ele/Ela tem que tomar uma decisão sobre o que fazer com o erro. Tratar ou repassar para a camada superior que pode lidar com isso.
- Pacote utilizado:
- intl
Existe um pluggin para vscode e android studio que ajuda na implementação dessas strings para internacionalização do app e a própria documentação desta extensão mostra como utilizá-la.
- Crashlitics
- Analytics
- Tagueamento
- Testes unitários
- Testes de widget
- Golden Test
Ainda será preciso verificar a situação deste app trabalhar com vários flavors simultaneamente, e neste caso em duas situações:
- Um main.dart para cada flavor
- Um único main.dart porém com marcas segmentadas internamente no app/
- E ainda, como lídar com cache das informações? Cache strategy
- Toast Messages