Micro Frontend in Flutter: Modularization Application

TungND
6 min readMay 3, 2024

--

Micro Frontend Application with Flutter

In the world of app development, we’d asked ourselves: How can we manage features when the app becomes bigger?

Problems

To ask the previous question, we can choose the best state management (of course, fit with business) and use Clean Architecture for the whole app, but sometimes, we still encounter some confusion. During 4 years of working and researching structured mobile apps, I’ve had some problems when developing a super-app:

  • Language file: When building a large app, we can’t avoid making a ‘giant’ language file, controlling it is a struggle work. We need to name each key following the template [module]_[screen]_[name] to make sure the key will not be duplicated. The next problem is resolving conflict when 2 separate features use the same language files. I’ve researched many ways to resolve this issue, using l10n and intl_utils, but both don’t support using multiple language files.
We need to have the prefix for each key, and with a big project, we will have thousands of keys in one file
  • Naming file: we also have the naming convention, like [module]_[screen]_[function].dart or class ModuleScreenFunction.When meeting some modules with a long name, we also have the long name of each file, and the prefix of them (module name) is the same. I hate it.
Need to separate each module into a directory, also naming each file with a prefix
  • Clear about dependencies, directories, and import: Using all directories in the same app, we can’t manage import, because all places we can use each other, even other modules. I’ve met a junior developer copy a whole old feature and edited it to make a similar feature. Because of not checking carefully, the new module still has some code that calls to the old module, damm it.

So I’ve researched about Module and Package: Can we make one feature into a separate module? So another module that depends on just needs to call Module.start() and handle the results. The high-level module doesn’t know the logic inside the low-level module - this concept looks like Facade design pattern. Of course, each module has its language file, and without export, a module can only access all classes inside its module, so no need to have the prefix .
That’s clearly, I had some ray of light in the darkness. In the next step, I will clear some other advantages and try to find some disadvantages when we migrate the traditional project into modularization.

1. Advantage

  • Each module/feature can be developed, tested, and maintained independently. So we can separate the team for each module, and just test and code inside it. Perfect!
We can develop and test independently auth or product module
  • After separate modules, we can pick modules for build with each flavor. For example, with flavor A we can use module A, and with flavor A1 we can use module A1 for the same purpose without increasing app size. That’s crazy. Imagine that we have to build an app for management and an app for end-users, but the authentication is (very) different. We will build 2 modules, auth-management and auth-end-user, and pick the correct module when building the app.

2. Disadvantage

  • The biggest problem when modularization is about dedicated. In the ideal case, each module will be responsible for one feature, and we can merge them inside Main Application. But in real life, we have to make some use cases by combining some pieces in many modules. They will make us struggle to think about which module needs adding the code, can be reused in the future, and doesn’t make any side effect to the old feature.
  • Decide to create a new module or reuse an old module with a customizing is also a problem that needs to be thought before implementing

Ok, that’s enough for the research phase. I think you should try it, at least if not successful, we will learn many things about managing modules and packages in Flutter.

Idea

1. Shared module

First, just like the traditional project, we try to find some code that can be reused multiple times and make them inside one place. They can be logic, common widgets, or utility functions. They can be packaged into one module (named like shared), so other modules can implement it and use it.
I just want everything will be clear, even the shared module. Day by day, shared module will be bigger if we put anything that is shared from all modules into it. So I separate it into 2 modules, core and ui.

  • ui just have only common UI, like theme, text, image, form, etc… and only they, not have any big logic. We can dependencies some libs about UI here, like form_builder, url_launcher, table_calendar, and fonts. Remember one point, ui just have UI class, not anymore. With other widgets having to implement expensive logic (like calling API, or using cache,…) we will talk later.
  • core will have only shared logic. It can be some utility classes, base classes, handle exceptions, base calling API, base state management… something like domain layer for a super app.

I think that’s enough for the shared module.

2. Functional module

  • A functional module should dependencies both shared modules, and use exported element to implement its feature.
  • Each function module still follows Clean Architecture, and we will try to export the smallest number of functions outside. Ideally, it just needs to export a start function to open it from the other, and LocalizationsDelegate for language change. For some specific cases, we will need to export other functions or logic, but remember the principle: Try not to export too many things outside.

3. Main application

Of course, we still need to have main() and startApp() function, outside all modules, and the main responsibility is merging modules into a complete app. For example:

  • Splash screen: We can create and initial everything from other modules.
  • Home screen: In my opinion and business, home is a screen that just shows something from different modules. Maybe it still has logic, but it is just a screen where we combine many widgets from other modules.

4. Some other shared modules

In some cases, we still need to create some other shared module, but not use it in all dependencies. For example:

  • We will need to implement a picker widget, it picks an image/file from storage, and then gets a link by uploading to the server side. In this case, we still need to use both ui and core module, so I create a separate module: storage . I will have all the logic about picking images, calling API upload, and maybe compressing, cutting photos,.. in the future.
  • We have to use Firebase Remote Config to get some configures from Firebase, and can be reused in many other modules. Of course, we can add it inside thecore module, but as I said, this function isn’t used in all modules, and I try to make everything clear. So I create config module and export functionalities to others.

Decide to create a new module, or create a new function to an old module, that depends on your business and your team. If you are not sure about your decision, you can make a poll to the team and get the results :))

Implementation

I think that’s enough for us to start implementing. Make sure that you note everything have to do before starting.
Implementation will be a long story, so I will have part 2 to give more detail about my opinion and how I think when making modulization.

If you learned something from this article, don’t forget to give it a thumbs up!!!

Update: Now part 2 is released, visit and get the implementation
Micro Frontends in Flutter: Modularization Application -Part 2

--

--