Microapps architecture com Dart/Flutter

Search for a command to run...

No comments yet. Be the first to comment.
Como integrei AWS IoT Core em um app Flutter: desafios e aprendizados

Se o seu projeto Flutter estiver utilizando uma versão do Gradle inferior à 8, é provável que você enfrente problemas ao usar o Android Studio 2024.2. Isso ocorre porque essa versão do Android Studio agora utiliza, por padrão, o JDK 21. Para solucion...

Você já enfrentou o desafio de fazer um aplicativo Flutter usar recursos nativos? Muitas vezes, isso pode ser um tanto traumático. Certa vez, precisei fazer com que um aplicativo usando React Native + Expo utilizasse o GPS em um dispositivo de pagame...

Introdução Depois de contribuir para um package da comunidade(por ex. flutter_zxing) tive a curiosidade de me aprofundar mais em como chamar código nativo direto de um projeto Dart, e também estou tendo cada vez mais interesses em linguagens como Rus...

A arquitetura de microapps tem como objetivo proporcionar ao usuário uma experiência unificada, utilizando diversos microapps altamente especializados.
Com Flutter/Dart, temos a oportunidade ideal para criar projetos com essa arquitetura, devido à comunidade madura e ao excelente sistema de gerenciamento de pacotes e dependências disponível.
Ao pensar em microapps, é importante modularizar e especializar partes de um aplicativo maior. Para agilizar o desenvolvimento, é necessário buscar ferramentas que possam ajudar nesse processo.
Uma opção para modularização é o Flutter Modular, que é amplamente conhecido e robusto. Ele permite a modularização e oferece um sistema de Injeção de Dependência (DI) interessante, sem que seja necessário se preocupar com rotas. Outra opção é o Module Provider, que também é bastante robusto e ajuda a modularizar, a utilizar DI e a navegar entre as telas.
Mesmo que existam esses e outros pacotes para ajudar na modularização, em muitas situações pode ser necessário criar nossa própria solução para alcançar esse objetivo. Eu mesmo já gastei algumas horas desenvolvendo algo semelhante ao Flutter Modular e ao Module Provider, construindo uma fachada para o Go_router e um sistema de DI semelhante ao Kiwi ou ao Get_it (que, sim, funcionou! 🤣).
Ao contrário da arquitetura de micro serviços, a arquitetura de microapps muitas vezes requer que vários microapps sejam agregados em um único super app, que pode ser construido e distribuido em um único arquivo. Para isso, pode ser necessário ter um projeto ou pacote que sirva como "cola", contendo todas as dependências dos microapps e serviços e, talvez, inicializando o MaterialApp e outras configurações comuns no "void main()".
Com essa abordagem, é possível versionar cada microapp e distribuí-los para diferentes equipes que cuidam de contextos diferentes. Onde trabalho hoje, optamos por um projeto monorepo e gerenciamos as versões usando tags no Git, mas estamos avaliando se precisamos criar um pub.dev personalizado (como o https://pub.dev/packages/unpub ou jFrog, por exemplo) para nossas necessidades específicas.
Uma vantagem de versionar seus microapps é que um rollback seria muito mais rápido e fácil. Em teoria é só fazer downgrade da versão do microapp nas depedencias dentro do seu projeto "Build".
Muitas pessoas optam por criar um pacote somente para gerenciar as versões de dependências globalmente, chamado de "shared_dependencies" na maioria das vezes. Todas as dependências comuns ficariam dentro deste pacote e seriam compartilhadas por todos os microapps.
Embora seja uma proposta interessante, na verdade é desnecessária. O próprio pub tool do Dart faz esse papel com maestria, como podemos ver em exemplos aqui e aqui.
Uma grande vantagem dessa abordagem arquitetural é que cada microapp pode ter suas próprias dependências. Por exemplo, um microapp pode usar somente o ValueNotifier/ChangeNotifier, enquanto outro pode usar MobX. No final, o pub tool resolve todas as dependências de forma eficiente e inteligente.
Aqui talvez tenha um ponto critico em sua arquitetura de microapps. Dependendo do ponto em que se quer chegar vários microapps podem ou não trocar dados, e como nós fazemos isso?
Uma das alternativas e uma das mais usadas é com Streams, talvez até usando o já conhecido EventBus, outra é usar um estado compartilhado encapsulado em um package separado, ou para ser mais simples também podemos enviar argumentos diretamente nas rotas, porém uma infinidade de alternativas podem ser desenvolvidas além destas.
Um ponto a ser destacado é que com Flutter Web dependendo da abordagem pretendida, a passagem de parâmetros via rotas(direto na URL) é muito melhor quando uma URL fica exposta ao publico ou pode ser compartilhada, com streams sua camada de apresentação e todo resto pode quebrar em algum momento.
Embora muitos desenvolvedores tenham reclamado sobre o build_runner, eu também já tive minhas dificuldades com ele. No entanto, minha experiência com a arquitetura de microapps foi bastante positiva. É possível executá-lo isoladamente em cada microapp, que teoricamente é um pacote diferente, tornando-o muito mais rápido e eficiente.
Outra vantagem dessa arquitetura é a possibilidade de separar a lógica de negócios e os serviços da camada de apresentação. Por exemplo, podemos ter um pacote especializado para consumir uma API e conter as regras de negócio, enquanto outro microapp contém a camada de apresentação e suas reatividades.