DEV Community

Ali El-Khatib
Ali El-Khatib

Posted on Originally published at github.com

Architecting a Production-Grade Flutter Monorepo: LEGO Modular Boundaries, Melos & bloc_signals

When scaling Flutter applications from a hobby project to an enterprise-grade codebase, teams almost always hit one of two architectural extremes:

  1. The Monolithic Mess: All business logic, HTTP clients, and UI widgets are dumped into one or two folders. Global state is everywhere, modules are tightly coupled, and making a change in one screen breaks three others.
  2. The "Over-Abstracted" Clean Architecture: In an attempt to follow Clean Architecture strictly, developers create 15 nested folders (data sources, raw DTOs, mappers, domain entities, use cases, presenters) just to display a simple settings toggle.

To solve this dilemma, I designed and open-sourced Flutter Production Starter โ€” a modular, feature-first monorepo template built for real-world development speed and long-term maintainability.

In this article, Iโ€™ll walk you through the architectural principles, dependency boundaries, and modern stack choices behind this starter.


๐Ÿงฑ The Core Philosophy: "LEGO" Modular Boundaries

The fundamental rule of this architecture is LEGO Modularity: every feature should be a self-contained building block with a clear responsibility, minimal coupling, and an intentional public API.

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                  APPLICATION                  โ”‚
โ”‚ Bootstrap โ€ข Config โ€ข DI โ€ข Routing โ€ข Observers โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                        โ”‚
                        โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚               FEATURE MODULES                 โ”‚
โ”‚ Auth โ”‚ Profile โ”‚ Home โ”‚ Settings โ”‚ Payments   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                        โ”‚
                        โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                SHARED PACKAGES                โ”‚
โ”‚ app_core โ”‚ network โ”‚ storage โ”‚ design_system  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
Enter fullscreen mode Exit fullscreen mode

1. Feature-First Colocation

Instead of organizing the entire codebase by layer (data/, domain/, presentation/), all code belonging to a business capability is colocated in apps/mobile/lib/features/<feature>/.

2. Public API Barrel Files

A feature must never reach into the private implementation files of another feature. Instead, features expose only intentional contracts through their root barrel file:

// โœ… Clean: Importing through the feature's public API
import 'package:mobile/features/auth/auth.dart';

// โŒ Forbidden: Deep import into private data sources
import 'package:mobile/features/auth/data/datasources/auth_remote_data_source.dart';
Enter fullscreen mode Exit fullscreen mode

โš–๏ธ Pragmatic Clean Architecture (3 Complexity Tiers)

Clean Architecture should be applied where complexity justifies it, not blindly everywhere:

  • Tier 1 โ€” Simple Feature (e.g. settings): Presentation + State only. No need for use cases or DTOs when updating a local theme mode.
  • Tier 2 โ€” Medium Feature (e.g. profile): Entity contract, Repository implementation, DTO mapping, and presentation.
  • Tier 3 โ€” Complex Feature (e.g. auth): Complete Clean Architecture with Use Cases, Remote Data Sources, Token Storage, and Route Guards.
  • Pluggability (e.g. auth_v2): Proves you can swap out an entire feature's underlying data/service layer behind its domain interface via Dependency Injection without modifying consumer code.

๐Ÿ“ Monorepo Structure with Melos

Managing multiple packages in a single repository is powered by Melos:

/
โ”œโ”€โ”€ apps/
โ”‚   โ””โ”€โ”€ mobile/              # Main app (Bootstrap, Environments, DI, Routes, Features)
โ”œโ”€โ”€ packages/
โ”‚   โ”œโ”€โ”€ app_core/            # Result<T> monad, domain Failure taxonomy, Sanitized AppLogger
โ”‚   โ”œโ”€โ”€ app_network/         # Centralized Dio, interceptors, error mappers, ApiClient
โ”‚   โ”œโ”€โ”€ app_storage/         # SecureStorage, KeyValueStorage, TTL MemoryCache
โ”‚   โ”œโ”€โ”€ design_system/       # Tokens (Spacing, Radius), Light/Dark themes, Primitives
โ”‚   โ””โ”€โ”€ app_lints/           # Strict linting & static analysis configuration
โ”œโ”€โ”€ melos.yaml               # Monorepo scripts (analyze, test, format, run)
โ””โ”€โ”€ ARCHITECTURE.md          # Architectural guide
Enter fullscreen mode Exit fullscreen mode

Why Dedicated Shared Packages?

  • app_core: Pure Dart abstractions (Result<T>, Failure, sanitized logger) with zero Flutter/UI dependencies.
  • app_network: Centralized Dio instance. Features never instantiate Dio() directly. It owns token injection, exponential backoff retries, and automatic sensitive data redaction (passwords and bearer tokens are never logged in plain text).
  • design_system: Standalone visual primitives, tokens (AppSpacing, AppRadius, AppDurations), and complete Material 3 Light/Dark themes.

โšก Modern Technology Stack

Capability Library / Solution Rationale
Routing kaisel: ^1.1.0 Strongly-typed declarative routing and route guards (AuthRouteGuard).
State bloc_signals + signals_flutter Fine-grained reactive signals without boilerplate.
DI get_it + injectable Constructor injection with compile-time code generation.
Networking dio: ^5.11.0 Enterprise HTTP client encapsulated in app_network.
Models freezed Immutable union states, DTOs, and copyable entities.
Feedback toastification: ^3.2.0 Clean presentation-only feedback and snackbars.

๐Ÿ›ก๏ธ Functional Error Pipeline

Instead of leaking raw HTTP exceptions into widgets, the app uses a functional Result<T> and Failure hierarchy:

// Fetching data cleanly with functional Result
final result = await loginUseCase(email: email, password: password);

result.fold(
  onSuccess: (session) => router.toHome(),
  onFailure: (failure) {
    // FailureMessageResolver resolves friendly messages
    feedback.showError(context, failureResolver.resolve(failure));
  },
);
Enter fullscreen mode Exit fullscreen mode

๐Ÿงช Testing & CI

A starter is only as good as its verification. Every package in the monorepo has automated tests and strict linting:

# Run tests across all 6 packages simultaneously
melos run test

# Check static analysis across the entire monorepo
melos run analyze
Enter fullscreen mode Exit fullscreen mode

GitHub Actions CI is already pre-configured (.github/workflows/ci.yml) to validate every commit and PR automatically.


๐Ÿš€ Try It Out

The entire template is open-source under the MIT License!

๐Ÿ‘‰ GitHub Repository: https://github.com/Ali-El-Khatib/flutter-production-starter

# Clone the repository
git clone https://github.com/Ali-El-Khatib/flutter-production-starter.git

# Bootstrap packages
cd flutter-production-starter
melos bootstrap

# Run the app
melos run run:dev
Enter fullscreen mode Exit fullscreen mode

If you find this architecture helpful for your Flutter projects, feel free to give it a โญ on GitHub and share your thoughts in the comments below! What architectural patterns do you prefer for large-scale Flutter apps?

Top comments (0)