🌍 Choose Language: English | Tiếng Việt
Welcome to the core technical documentation of the Codebase Provider Monorepo! This is a large-scale, highly sustainable, and modular industrial Flutter application architecture. The system is designed based on the Micro-packages Monorepo model, strictly combining Clean Architecture, SOLID principles, and supporting multi-state management systems (MVVM + Provider and BLoC).
This project uses Dart's native Pub Workspaces, allowing for dependency optimization, feature independence, and automated CI/CD right at the project root.
Template disclaimer: Feature / domain / data packages shipped in this repo (Auth, Home, Settings, Onboarding, Splash, Dashboard, Language, etc.) are sample reference code that demonstrate Clean Architecture wiring. Treat them as patterns to copy or delete when building a real product — not as production business logic. Agent rules live in
.agents/AGENTS.md.
The layer decomposition in the Monorepo is strictly organized from Core (Infrastructure) ➔ Domain (Core Business) ➔ Data (Integration Implementation) ➔ Features (Feature UI/Screens):
graph TD
classDef core fill:#f9f2f4,stroke:#d0a9b5,stroke-width:2px,color:#333;
classDef feature fill:#eef7fa,stroke:#a6c8df,stroke-width:2px,color:#333;
classDef domain fill:#f4faee,stroke:#b5d4a6,stroke-width:2px,color:#333;
classDef data fill:#fff3e6,stroke:#f5cb99,stroke-width:2px,color:#333;
classDef app fill:#f0f0f0,stroke:#cccccc,stroke-width:2px,color:#333;
App["🚀 Host App Shell (app/)<br/>Assembles the application"]:::app
subgraph FeatureLayer ["🎨 Feature Presentation Layer (packages/features/*)"]
direction LR
FeatSplash["splash"]:::feature
FeatAuth["auth"]:::feature
FeatDash["dashboard"]:::feature
end
subgraph DataLayer ["🔌 Data Layer (packages/data/*)"]
direction LR
DataCore["data_core"]:::data
DataAuth["data_auth"]:::data
end
subgraph DomainLayer ["⚙️ Domain Layer (packages/domain/*)"]
direction LR
DomCore["domain_core"]:::domain
DomAuth["domain_auth"]:::domain
end
subgraph CoreLayer ["🛠️ Core Infrastructure Layer (packages/core/*)"]
direction LR
CoreUI["core_base_ui"]:::core
CoreCom["core_common"]:::core
CoreNet["core_network"]:::core
CoreStore["core_storage"]:::core
CoreDB["core_database"]:::core
CoreDI["core_di"]:::core
CoreKit["core_ui_kit"]:::core
end
%% Cross-layer Relationships
App -->|"Imports & Initializes"| FeatureLayer
App -->|"Imports & Initializes"| DataLayer
App -->|"Imports & Initializes"| DomainLayer
App -->|"Imports & Initializes"| CoreLayer
FeatureLayer -->|"Triggers UseCases & Entities"| DomainLayer
DataLayer -->|"Implements Repository Contracts"| DomainLayer
FeatureLayer -.->|"Uses Tokens/Widgets/DI"| CoreLayer
DataLayer -.->|"Uses API/DB/Cache mechanisms"| CoreLayer
%% Domain sits at the centre and depends on NOTHING.
%% Core may depend on Domain — never the reverse.
CoreCom -.->|"Uses Result / AppFailure"| DomCore
CoreDI -.->|"Uses UserEntity in contracts"| DomAuth
Important
Domain depends on nothing. domain_core declares zero workspace dependencies and no
domain package declares the Flutter SDK — AppFailure lives in domain_core alongside
Result<T>. Arrows into Domain (core_common → domain_core, core_di → domain_auth) are the
only approved upward edges; see reference/01_rules.md.
Below is the complete physical organization structure of the Workspace:
/ (Workspace Root)
├── .github/ # Continuous Integration workflows (CI Workflows)
│ └── workflows/
│ └── fastlane.yml # CI Github Action running Fastlane automatically
├── app/ # Host Application (Main App Shell)
│ ├── android/ # Native Android project
│ ├── ios/ # Native iOS project
│ ├── lib/
│ │ ├── config/ # Environment configurations (Flavors dev, staging, prod)
│ │ ├── di/ # Central DI registration point (injection.dart)
│ │ ├── presentation/
│ │ │ ├── navigation/ # GoRouter assembly (app_router.dart) + shell widgets
│ │ │ ├── providers/ # App-shell globals (AppProvider, DeeplinkProvider)
│ │ │ └── widgets/ # NavigatorWrapperWidget, UndefineRouteWidget
│ │ ├── main.dart # Main app entrypoint
│ │ └── main_scope.dart # Boot Lifecycle Management (Splash → RootApp)
│ └── pubspec.yaml # Host App config (links all sub-packages)
├── packages/ # Contains Micro-packages
│ ├── core/ # Shared infrastructure — MECHANISM ONLY, never feature data
│ │ ├── base_ui/ # Theme, LanguageProvider, design tokens & l10n (zero widgets)
│ │ ├── bloc_state_management/ # BaseBloc, BaseCubit, BlocViewState<T>
│ │ ├── common/ # Enums, ErrorHandler, AppConfig, extensions, src/utils/
│ │ ├── database/ # Drift mechanism: IDatabaseHandle, IDatabaseMigration, opener
│ │ ├── di/ # DI Hub — every cross-package contract lives here
│ │ ├── network/ # Dio + Retrofit factory, interceptor chain, SSL pinning
│ │ ├── notifications/ # Push Notification management module
│ │ ├── provider_state_management/ # BaseProvider, executeOperation, ViewStateModel
│ │ ├── storage/ # StorageManager + StorageValue<T> (defines NO keys)
│ │ └── ui_kit/ # core_ui_kit — reusable widgets every feature may use
│ ├── domain/ # Pure Dart business Micro-packages — ZERO dependencies
│ │ ├── core/ # Result<T>, AppFailure, BaseEntity, BaseUseCase
│ │ ├── auth/ # Entities, UseCases, Repository interfaces for Auth
│ │ └── language/ # Entities, UseCases for multi-language
│ ├── data/ # Integration implementation Micro-packages
│ │ ├── core/ # IBaseRepository + CacheDatabase (owns its own tables/DAO)
│ │ ├── auth/ # Models, DataSources, RepositoryImpl for Auth
│ │ └── language/ # RepositoryImpl for multi-language
│ └── features/ # Independent feature packages (Feature Packages)
│ ├── splash/ # Splash Feature (sample): Startup loading screen
│ ├── onboarding/ # Onboarding Feature (sample): New user guide
│ ├── auth/ # Auth Feature (sample): Login, Register, Forgot Password
│ ├── dashboard/ # Dashboard Feature (sample): Shell chrome only (Bottom Tab host)
│ ├── home/ # Home Feature (sample): Home Tab
│ └── settings/ # Settings Feature (sample): Settings Tab (separate from Home)
├── tools/ # Command-line toolset for developers
│ ├── android_compliance/ # 16KB Page Size compatibility check (Android 15+)
│ ├── barrel_generator/ # Script to auto-generate barrel files for packages
│ ├── code_review/ # Gemini AI integrated automated source code review tool
│ ├── firebase/ # Automated Firebase environment configuration
│ ├── module_generator/ # CLI to generate new Feature/Domain/Data/Core packages
│ ├── theme_generator/ # Auto-generate Splash Screen & App Icons
│ ├── unused_checker/ # Analyze & clean unused files, assets, translations
│ ├── workspace_setup/ # Workspace setup script (pub get, build_runner, l10n)
│ ├── dependency_sync.dart # Sync library versions from centralized catalog
│ └── check_outdated.dart # Check for outdated libraries on pub.dev
├── pubspec.yaml # Pub Workspace configuration (workspace: [...])
├── pubspec_dependencies.yaml # Single source of truth for library versions (Version Catalog)
└── README.md # This Master Technical Manual
Note
Every package owns a utils/ folder holding its own constants — storage keys, route
paths, timeouts. Nothing domain-specific belongs in core_common. The single approved
exception is the design-token set under core_base_ui/src/styles/, which stays put because it
is the public surface of the design system.
All tools can be run from the root directory.
- Module Generator (
tools/module_generator/):# Create Feature package 'profile' using Provider: dart tools/module_generator/generate.dart 1 profile "" 1 # Create Domain micro-package 'payment': dart tools/module_generator/generate.dart 2 payment # Create Data micro-package 'payment': dart tools/module_generator/generate.dart 3 payment
- Dependency Sync (
tools/dependency_sync.dart):dart tools/dependency_sync.dart # Sync version dart tools/dependency_sync.dart --check # Check only
- Check Outdated (
tools/check_outdated.dart):dart tools/check_outdated.dart # Check outdated libraries on pub.dev - Barrel Generator (
tools/barrel_generator/):dart tools/barrel_generator/generate.dart packages/features/profile/lib
- Workspace Setup (
tools/workspace_setup/):dart tools/workspace_setup/configure.dart # cross-platform - Code Review AI (
tools/code_review/):dart tools/code_review/code_review.dart --all
- Unused Checker (
tools/unused_checker/):dart tools/unused_checker/check_script.dart
- Theme & Firebase:
dart tools/theme_generator/theme_setting.dart dart tools/firebase/firebase_config.dart
- Domain Layer (
packages/domain/*):- Pure Dart, enforced by the package graph — not merely by convention.
domain_corehas zero workspace dependencies and none of the three domain packages declares the Flutter SDK. - Do not import
flutter/material.dart,dio,retrofit, or any UI/Network library. - Defines
Entities,UseCases,Repository Interfaces,Result<T>andAppFailure.
- Pure Dart, enforced by the package graph — not merely by convention.
- Data Layer (
packages/data/*):- Implements contracts from the
domain. - Uses
core_network(API),core_storage(key-value) andcore_database(SQL) as mechanisms — each data package declares its own storage keys and its own database. - DataSources return Models, never Entities, and never expose Drift-generated row classes.
- Transforms Models → Entities via the
.toEntity()function.
- Implements contracts from the
- Presentation Layer (
packages/features/*):- Renders UI and manages state (Provider or BLoC).
- Only communicates with Domain through UseCases, absolutely no direct API calls.
- FORBIDDEN to depend on the
datalayer or on any other feature package — no exception; shared widgets come from the core packagecore_ui_kit.
- Core Layer (
packages/core/*):- Supplies mechanism only. FORBIDDEN to depend on any
feature_*ordata_*package. - May depend on
domain_*(Domain is the centre):core_common → domain_core,core_di → domain_auth,provider_state_management → domain_core.
- Supplies mechanism only. FORBIDDEN to depend on any
Important
Any feature can be deleted and the app still boots. Everything the shell consumes at runtime
resolves through a core_di contract behind getItOrNull / getAllOrEmpty with a safe fallback.
getAll<T>() throws when nothing is registered — always prefer getAllOrEmpty<T>().
Features communicate across each other entirely through intermediate interfaces in core_di:
[Feature Auth]
│
▼ (Requests redirection to Home)
[Interface HomeNavigator (core_di)] ◄── (Contract definition)
▲
│ (Concrete implementation in the owning feature)
[HomeNavigatorImpl (packages/features/home/lib/src/routing/)]
Cross-feature UI actions (e.g. logout) use the same DIP shape with I*ActionHandler in core_di and *ActionHandlerImpl inside the owning feature (feature_auth/handlers/).
Each micro-package is responsible for its own DI configuration using injectable:
import 'package:injectable/injectable.dart';
@InjectableInit.microPackage()
void initMicroPackage() {}const _coreModules = [
ExternalModule(CoreCommonPackageModule),
ExternalModule(CoreNetworkPackageModule),
ExternalModule(CoreNotificationsPackageModule),
ExternalModule(CoreStoragePackageModule),
// Registers nothing: `core_database` is mechanism only and owns no database.
ExternalModule(CoreDatabasePackageModule),
ExternalModule(CoreDiPackageModule),
];
// CoreBaseUiPackageModule depends on ILanguageStorage / IThemeStorage
// (app-local singletons). Register it in externalPackageModulesAfter.
const _uiModules = [
ExternalModule(CoreBaseUiPackageModule),
];
const _domainModules = [
ExternalModule(DomainCorePackageModule),
ExternalModule(DomainAuthPackageModule),
ExternalModule(DomainLanguagePackageModule),
];
const _dataModules = [
ExternalModule(DataCorePackageModule),
ExternalModule(DataAuthPackageModule),
ExternalModule(DataLanguagePackageModule),
];
// The app shell's ONLY intentional hard reference to feature packages —
// as the composition root it must name what it composes.
const _featureModules = [
ExternalModule(FeatureAuthPackageModule),
ExternalModule(FeatureDashboardPackageModule),
ExternalModule(FeatureHomePackageModule),
ExternalModule(FeatureOnboardingPackageModule),
ExternalModule(FeatureSettingsPackageModule),
ExternalModule(FeatureSplashPackageModule),
];
const _otherModules = [
ExternalModule(ProviderStateManagementPackageModule),
ExternalModule(BlocStateManagementPackageModule),
];
@InjectableInit(
externalPackageModulesBefore: [..._coreModules],
externalPackageModulesAfter: [
..._uiModules,
..._domainModules,
..._dataModules,
..._featureModules,
..._otherModules,
],
)
Future<void> configureDependencies({String? environment}) async {
getIt.enableRegisteringMultipleInstancesOfOneType();
final env = environment ?? AppConfig.appFlavor.toValue();
await getIt.init(environment: env);
}Caution
An eager @Singleton must not depend on a type registered by a later module — it throws
"not registered" at boot. flutter analyze cannot catch this; verify against the generated
app/lib/di/injection.config.dart. Use @LazySingleton when the dependency lands later.
GetIt does not resolve supertypes. Registering Impl as InterfaceA leaves
getIt<InterfaceB>() unresolvable even when InterfaceA implements InterfaceB — bind the second
interface explicitly through an @module (see app/lib/di/network_binding_module.dart).
We use go_router combined with go_router_builder to ensure type-safe routing and maximum source code fragmentation.
Each Feature Package owns its own routing structure and files:
SplashPageis hosted byMainScopeduring boot and is not registered in GoRouter.- The
feature_authpackage owns the route groupAuthShellRoute,LoginRoute,RegisterRoute,ForgotPasswordRoute. - Routes inherit from
GoRouteDataCustomto inherently possess automatic screen tracking and smooth cross-platform transitions.
app/lib/presentation/navigation/app_router.dart does not hardcode $onboardingRoute / $homeShellRoute lists. It collects:
getAllOrEmpty<IFeatureRouteModule>()→ top-level stack routes (auth, onboarding, …) — noordergetAllOrEmpty<IDashboardTabModule>()sorted byorder→StatefulShellBranchlistgetItOrNull<DashboardRouteModule>()→ dashboard chrome (optional)getItOrNull<IAppEntryLocation>()?.path→initialLocation(else first tab //)getItOrNull<IAuthRefreshListenable>()→refreshListenable
Note the last one: the router depends on a core_di contract, not on AuthProvider. The shell
holds no feature type at all, which is what makes feature_auth removable.
- Delete its
ExternalModule(...)entry and matching import inapp/lib/di/injection.dart. - Delete its
feature_x:entry inapp/pubspec.yaml. - Delete its path from the
workspace:list in the rootpubspec.yaml. flutter pub get && dart run build_runner build -d --workspace.
No other file needs editing — every runtime lookup falls back safely. See
guides/04_routing.md.
The CI/CD system utilizes Fastlane with the Workspace-Root Delegation architecture:
fastlane android build flavor:dev build_type:apk distribute_store:false distribute_firebase:false skip_setup:true change_log:test build_number:1 flutter_version:stable version:1.0.0- Forbidden to Use
printCommand: All CLI Tools intools/must usestdout.writeln(...)andstderr.writeln(...). - Forbidden to Disable Linter Warnings: Do not use
// ignore_for_file: avoid_print.
- Flutter: >= 3.47.1 (Stable)
- Dart SDK: >= 3.13.1
- JDK: 17
- Ruby: >= 3.0 (for Fastlane)
flutter pub getThanks to Pub Workspaces, all dependencies of the Host App and all sub-packages are fetched concurrently and create a single pubspec.lock.
packages/core/common/lib/src/firebase/firebase_module.dart imports all three
firebase_options_{dev,staging,prod}.dart files unconditionally, and they are git-ignored. Run
flutterfire configure once per flavor before the first build — see
getting-started/01_setup.md.
dart run build_runner build -d --workspaceflutter run -t app/lib/main.dart --flavor dev --dart-define-from-file=app/env.devcd app # required — building from the workspace root fails with a misleading Gradle error
flutter build apk --flavor dev --debug --dart-define-from-file=env.devWarning
flutter analyze excludes generated files (**.freezed.dart, **.g.dart, **.config.dart,
**.module.dart — see analysis_options.yaml). A clean analyze does not prove the app
compiles. Always run a real build before trusting a large refactor.
Start here → docs/en/README.md (Vietnamese: docs/vi/README.md)
The documentation is organised by what you are trying to do, not by layer.
| Doc | Answers |
|---|---|
| 01. Setup | What do I install, and how do I get the app running? |
| 02. Project Tour | What is every package for, and where do I change X? |
| 03. Daily Workflow | Which commands do I run, and when? |
| Doc | Covers |
|---|---|
| 01. Overview | Clean Architecture, the dependency rule, key trade-offs |
| 02. Core Layer | All nine core_* packages and what does not belong in them |
| 03. Domain Layer | Pure Dart mandate, Result<T>, entities, use cases |
| 04. Data Layer | Models, data sources, repositories, error conversion |
| 05. Feature Layer | Feature boundaries, structure, controller lifecycle |
| 06. App Shell | Boot lifecycle, DI assembly, dynamic router |
| Doc | Task |
|---|---|
| 01. New Feature | Scaffold a feature end to end |
| 02. New Domain + Data | Add a business capability |
| 03. State Management | Choose and use Provider or BLoC |
| 04. Routing | Register routes, navigate across features |
| 05. Dependency Injection | Scopes, module order, common traps |
| 06. Storage | Persist a value your package owns |
| 07. Database | Tables, DAOs, migrations (Drift) |
| 08. Networking | API client, interceptors, token refresh, SSL pinning |
| 09. Localization & Theming | Translations, design tokens, responsive sizing |
| 10. Cross-Feature Communication | The six sanctioned models |
| Doc | Contains |
|---|---|
| 01. Rules | Every architectural rule with its rationale |
| 02. Naming | File/class suffixes, folder conventions |
| 03. Tooling | Every script in tools/ |
| 04. Review Checklist | PR gate |
| Doc | Contains |
|---|---|
| 01. CI/CD | GitHub Actions & Azure pipelines, required secrets |
| 02. Fastlane & Release | Lanes, signing, store distribution |
AI-agent rules live separately in
.agents/AGENTS.mdand.agents/skills/.
Intellectual property rights belong to CaoGiaHieu-dev. All rights reserved.