lundi 18 octobre 2021

What's the best way to modularise reusable functionality across Flutter apps?

TLDR: When following clean architecture, when should a reusable piece of functionality be reused across different apps via a module vs a template, and how does one decide on the interface of a module?

Background

I'm currently writing some packages (for personal use when freelancing) for common functionality that can be reused across multiple Flutter apps and wondering what's a good way to organise them. With my apps I follow the clean architecture guidelines, splitting an app by features, with each feature consisting of data, domain and presentation layers:

|--> lib/
      |
      |--> feature_a/
      |       |
      |       |--> data/
      |       |      |
      |       |      |--> data_sources/
      |       |      |
      |       |      |--> repository_implementations/
      |       |      |
      |       |--> domain/
      |       |      |
      |       |      |--> repository_contracts/
      |       |      |
      |       |      |--> entities/
      |       |      |
      |       |      |--> use_cases/
      |       |      |
      |       |--> presentation/
      |       |      |
      |       |      |--> blocs/
      |       |      |
      |       |      |--> screens/
      |       |      |
      |       |      |--> widgets/
      |       |      |
      |--> feature_b
      |       |
      |       |--> ...

Example

If we take the user authentication feature, for example, I know that:

  • The entire domain layer, as well as the bloc, will be the same across most apps (email and password validation, authentication/login blocs, etc.)
  • The data layer will change depending on the backend/database
  • The screens/widgets will change with different UI's

Current Approach

My thinking is to write something like a single backend-agnostic "core_auth_kit" package, which contains the domain and bloc, and one package for each backend service I might use, e.g. "firebase_auth_kit", "mongodb_auth_kit", etc. Each backend-specific package will use the "core_auth_kit" as the outward-facing interface.

Here's how I plan on using this. If I'm writing a simple Firebase Flutter app, I will simply import the "firebase_auth_kit" package, and instantiate its auth_bloc at the root of the app inside a MultiBlocProvider, showing the login page if the state is "unauthenticated" and the home page if it's "authenticated". diagram of package structure

Questions

  1. What is the standard practice for deciding on the boundary of a module? i.e. is this approach of using the "highest common layer" (bloc in the authentication example) the way to go?
  2. When should a reusable piece of functionality be extracted as a template vs a module (is my example even a good candidate for a module, should it be a template instead)?

Thanks for your help

Aucun commentaire:

Enregistrer un commentaire