Flutter Interview Handbook

App Modularization

100%

App Modularization

As Flutter applications scale across large engineering teams, monolithic single-package codebases become difficult to maintain, leading to slow build times, accidental tight coupling, and merge conflicts. App Modularization solves this by splitting a codebase into decoupled, reusable Dart packages.


1. Feature-First vs Package-Based Monorepo Architecture

 [ Monolithic Codebase ]                        [ Package-Based Monorepo Architecture ]
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ lib/                   β”‚                    β”‚ apps/main_app (Entrypoint & Wiring)    β”‚
β”‚  β”œβ”€β”€ features/auth/    β”‚                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚  β”œβ”€β”€ features/pay/     β”‚                                         β”‚
β”‚  └── core/             β”‚                     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                     β–Ό                                       β–Ό
 (Easy to cross-import)               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                      β”‚ feature_auth    β”‚ ──(cannot import)──►│ feature_payment β”‚
                                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                               β”‚                                       β”‚
                                               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                                   β–Ό
                                                      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                                      β”‚ core_network / core_ui  β”‚
                                                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Feature-First Folder Structure (Single Package)

Organizes files by business domains inside a single lib/ folder: lib/src/features/authentication/ (domain, data, presentation).

  • Pros: Easy to start, low setup overhead.
  • Cons: Does NOT enforce hard import boundaries. Developers can accidentally import internal files from un-related features.

Package-Based Monorepo Architecture (Multi-Package)

Splits features into isolated local Dart packages inside a monorepo (packages/feature_auth/pubspec.yaml).

  • Pros: Compiler-enforced isolation. feature_auth cannot import feature_payment unless explicitly declared in pubspec.yaml. Faster incremental compilation.
  • Cons: Requires monorepo management tools (Melos).

2. Preventing Circular Dependencies & Barrel Files

In a package-based architecture, circular dependencies (Package A -> Package B -> Package A) fail at compile-time.

Strategies to Prevent Tight Coupling

  1. Decoupled Navigation: Use declarative routing (e.g. go_router) with string/enum paths instead of importing target screen widgets directly across feature modules.
  2. Core Contracts (core_foundation): Move shared data interfaces, event buses, and deep links into a shared core package that feature modules depend on.
  3. Strict Barrel Exports: Expose ONLY public API contracts via src/ barrel files (auth.dart). Keep implementation details private under lib/src/.
// lib/feature_auth.dart (Public API Barrel File)
export 'src/presentation/auth_screen.dart';
export 'src/domain/auth_user.dart';
// Internal data sources under src/ are NOT exported!

3. Monorepo Management with Melos

Melos is a CLI tool for managing multi-package Dart/Flutter repositories using pub workspaces.

Key Capabilities

  • Simultaneous Bootstrapping: melos bootstrap links local packages via pubspec.yaml without needing pub.dev publishing.
  • Cross-Package Script Execution: melos exec -- "flutter test" runs unit tests across 20+ packages concurrently.
  • Conventional Commits & Automated Versioning: Automatically generates CHANGELOG.md and bumps package semantic versions.
# melos.yaml
name: my_flutter_monorepo
packages:
  - apps/*
  - packages/*
  - core/*

scripts:
  test:
    run: melos exec --dir-exists="test" -- "flutter test"
    description: Run unit tests across all modular packages.

4. Trade-offs & Production Considerations

  • CI/CD Build Times: Modular packages drastically improve incremental compilation and CI build times by running tests and static analysis in parallel exclusively on modified packages.
  • Initial Setup Overhead: Monorepos require strict package boundaries, dependency management, and Melos configuration skills. Avoid over-modularizing premature codebases with fewer than 3 developers.