Uma abordagem de arquitetura em projetos Flutter

William Amaral
6 min readAug 7, 2022

--

Photo by Sung Shin on Unsplash

Este artigo é dividido em duas partes, confira aqui a continuação

Quando ouvimos falar sobre o assunto, logo pensamos nas melhores formas de estruturar os nossos projetos, como vamos organizar as pastas, dar nomes às coisas, mas muitas vezes (em quase todas elas), nós esquecemos que uma boa arquitetura não é tecnicamente sobre como vamos organizar as pastas ou que nome daremos àquela função que muitas vezes nem sabemos exatamente o que ela faz, mas sim, sobre qual problema precisamos resolver e de quais formas uma boa arquitetura pode nos ajudar. Então, é sobre isso que iremos conversar hoje, vamos lá?

Para início de conversa

Muito se fala em utilização de arquiteturas já conhecidas na comunidade, como a Clean Architecture, Onion, Hexagonal, enfim, existem inúmeras abordagens e opiniões diferentes sobre o assunto. Em termos gerais uma arquitetura bem definida deve nos proporcionar uma maior segurança em relação ao futuro, permitindo que novas funcionalidades sejam incluídas sem muitos problemas. Quanto à um modelo de arquitetura podemos citar:

  1. Possibilidade de escalar melhor o projeto ao longo do tempo.
  2. Simplicidade e facilidade em implementar ou dar manutenção em features.
  3. Delegar responsabilidades e isolar nosso código de forma que facilitem os testes.
  4. Simplificar e melhorar a Developer Experience.
  5. Definir melhores padrões e trazer consistência.

Tendo isso em mente, podemos partir para o próximo nível, o Flutter foi pensado para trazer mais simplicidade para o dia a dia da pessoa desenvolvedora, visto que o framework não traz consigo uma opinião formada sobre como devemos estruturar os projetos criados com ele, quais libs iremos utilizar, etc, como acontece em outras tecnologias como o Angular por exemplo, que é extremamente opinativo sobre como um projeto deve se comportar.

E não há nenhum problema com isso, cada tecnologia soluciona um problema específico, mas o Flutter, foi pensando para ser simples, ou seja, você pode usar a estrutura que quiser com seu projeto e tudo irá funcionar como deveria.

Mas isso não quer dizer, que não possamos pensar em boas formas de organizar e estruturar nossos projetos, se você estiver trabalhando em um projeto de pequeno porte, como um app de estudos, você pode usar arquiteturas simples e já conhecidas, como é o caso do bom e velho MVC (Model, View, Controller), ou abordagens um pouco mais recentes como o MVVM (Model, View, ViewModel).

Tudo vai depender do que você precisa no momento, e como as partes importantes do seu projeto irão se comunicar umas com as outras, agora, pensando em aplicações de médio e grande porte, que possuem um grande potencial de escalabilidade, é importante darmos um pouco mais de atenção sobre os conceitos arquiteturais que iremos seguir, quanto mais cedo conseguirmos pensar sobre isso, mais fácil será o nosso trabalho no futuro.

Encontrando um desafio

Recentemente, me deparei com um desafio técnico em um projeto Flutter (veja sobre isso aqui), que faz muito sentido com o que estamos falando. A aplicação vêm crescendo e se tornando cada vez mais complexa de lidar, com novos fluxos e funcionalidades entrando, é razoável refletir sobre a estrutura que o projeto está seguindo.

A aplicação foi criada seguindo os conceitos da Arquitetura Limpa, ou seja, todo o código foi separado em camadas específicas, começando pelo domínio, usecases, repositories, data sources, enfim. Todos os conceitos que a proposta dessa arquitetura sugere.

Mas, depois de um tempo, o que era pra ser uma ajuda, acabou dificultado um pouco as coisas, primeiro, o projeto estava organizado no conceito de layer-first, em resumo, todas as features estavam separadas por camadas, como está representado abaixo:

lib
|
|__presentation
| |__featureA
| |__featureB
|
|__domain
| |__featureA
| |__featureB
|
|__data
| |__featureA
| |__featureB

Essa organização estava gerando mais complexidade ao adicionar ou modificar alguma coisa, visto que, por mínima que fosse, era preciso mudar em três camadas diferentes (existiam outras ainda, mas isso não vêm ao caso agora). Sem falar que uma estrutura assim pode dificultar o trabalho no futuro, quando muitas pessoas trabalham nos mesmos locais, a chance de dar conflitos é enorme.

Em alguns casos existiam códigos que deveriam ser reutilizados em outros locais da aplicação, mas estavam localizados em locais inapropriados, ou misturados com outras classes num mesmo local, isso deixa uma certa preocupação, pois a tendência, é toda essa confusão aumentar se não der uma certa atenção a ela, logo no início.

Visto isso, faz total sentido pensar em uma abordagem um pouco diferente, a ideia basicamente é encontrar uma estrutura que satisfaça as dores e expectativas quanto à escalabilidade do projeto e que também facilite os testes. Para isso, o projeto pode manter alguns conceitos da Clean Architecture e combinar com outras sugestões arquiteturais como MVVM e a Android Architecture.

A palavra chave de uma boa estrutura é divisão de responsabilidades, algo que pode ser visto no S do SOLID (ou responsabilidade única)

A solução

Considerando os pontos discutidos mais acima, essa nova proposta possui três camadas principais, como você pode ver na imagem abaixo. Da esquerda para a direita, respectivamente:

Diagrama representando o fluxo da nova arquitetura do projeto Flutter, da esquerda para a direita, sendo eles: UI Layer, Domain Layer e Data Layer. Na imagem existem alguns balões representando cada parte das camadas, como exemplificado abaixo.
Proposta de nova arquitetura para o projeto

Camada de UI

  • Responsável por exibir os dados do aplicativo na tela, sempre que algo mudar, seja uma interação da pessoa usuária, ou uma entrada externa, a UI será atualizada para refletir as mudanças.
  • Possuem os State holders, responsáveis por gerenciar o estado de determinada interface e redirecionar as requisições da UI para a camada mais interna responsável.

Camada de Domínio

  • Está é uma camada opcional (nem todos os projetos ou fluxos precisam dela), sua responsabilidade é abstrair e encapsular uma regra de negócio do app complexa ou que precise ser reutilizada em vários locais.
  • As classes aqui são chamadas de Use Cases (Casos de Uso).
  • Cada caso de uso é responsável por apenas uma funcionalidade.
  • As entidades (modelo de dados) do projeto também ficam aqui.

Camada de Dados

  • Responsável por determinar como o app irá criar, armazenar e mudar os dados.
  • É composta por repositórios, que podem usar mais de uma fonte de dados (remota ou local), aqui também são feitas as conversões de dados externos (RAW) em entidades da aplicação.

Uma estrutura como essa nos permite separar e organizar melhor o código, e também pode trazer alguns benefícios, como: uma melhor experiência de desenvolvimento, visto que adicionar ou modificar alguma parte do código, se dará sem muitas dores de cabeça, melhor robustez do projeto, com processos e conceitos bem definidos e a possibilidade de termos um “baixo acoplamento” entre as partes.

Finalizando

Não é só porque existe uma arquietura que todas as pessoas dizem ser a mais correta para os projetos, ou que precisamos seguir ela fielmente, que deve ser assim. Na área de desenvolvimento as coisas mudam muito rápido, e sempre temos requisitos diferentes para resolver problemas diferentes, o interessante é que isso nos permite errar e aprender com esses erros.

No fim das contas, a melhor arquitetura, será aquela que vai tornar a sua vida de dev mais fácil e com menos preocupações quando for dormir 😄. O que na maioria das vezes acontece, é adaptarmos as sugestões arquiteturais que existem, para o nosso contexto.

No próximo artigo, irei detalhar como cada uma dessas camadas funcionam na prática e como elas se comunicam entre si, se for do seu interesse, fique à vontade para dar uma lida na segunda parte também!

Então, pra você que chegou até aqui, fica o meu muito obrigado por dedicar esses minutos de leitura, e caso ficou em dúvida ou queira conversar sobre o assunto, pode me procurar! Te vejo na próxima!! :)

--

--

William Amaral

Software Engineer | Passionate about Technology and Philosophy