# Directory Structure > How Flutter app code is organized in this project. --- ## Overview This Flutter app follows a **feature-first architecture** with clear separation of concerns: - **Feature modules** in `features/` for bounded product capabilities - **Core infrastructure** in `core/` for cross-feature protocols - **Shared UI components** in `shared/` for reusable widgets - **Data layer** in `data/` for infrastructure abstractions --- ## Directory Layout ``` apps/lib/ ├── main.dart # Only root entry file ├── app/ # App bootstrap & DI │ ├── di/ # Dependency injection setup │ ├── router.dart # Route definitions │ └── app.dart # App configuration ├── core/ # Cross-feature infrastructure │ ├── auth/ # Session store, auth state │ ├── config/ # Env configuration │ ├── logging/ # Structured logging │ └── network/ # HTTP client, error mapping ├── data/ # Shared infrastructure ONLY │ ├── cache/ # Cache implementations │ ├── network/ # Network adapters │ └── storage/ # Local storage ├── features/ # Feature modules │ ├── auth/ # Authentication feature │ ├── home/ # Home feature │ ├── divination/ # Divination feature │ ├── settings/ # Settings feature │ └── ... # Other features ├── shared/ # Reusable UI components │ ├── widgets/ # Shared widgets │ └── theme/ # App theme, design tokens └── l10n/ # Localization ``` --- ## Module Organization ### Feature Module Structure Each feature follows consistent structure: ``` features// ├── data/ # Data layer │ ├── apis/ # API clients │ ├── repositories/ # Repository implementations │ ├── services/ # Feature-specific services │ └── models/ # Data models/DTOs └── presentation/ # Presentation layer ├── bloc/ # State management (BLoC/Cubit) ├── screens/ # Screen widgets └── widgets/ # Feature-specific widgets ``` **Example: `features/auth/`** ``` features/auth/ ├── data/ │ ├── apis/ │ │ └── auth_api.dart # HTTP API calls │ ├── repositories/ │ │ └── auth_repository.dart # Repository implementation │ └── models/ │ ├── auth_user.dart # User model │ └── session_response.dart # Session DTO └── presentation/ ├── bloc/ │ ├── auth_bloc.dart # AuthBloc (ChangeNotifier) │ └── auth_state.dart # AuthState └── screens/ └── login_screen.dart # Login screen widget ``` ### Core Module Structure **Core contains cross-feature infrastructure:** ``` core/ ├── auth/ │ └── session_store.dart # Global session management ├── config/ │ └── env.dart # Environment configuration ├── logging/ │ ├── logger.dart # Logger interface │ ├── log_service.dart # LogService implementation │ ├── log_entry.dart # Log entry model │ └── error_handler.dart # Global error handler └── network/ ├── api_problem.dart # RFC7807 error model └── api_problem_mapper.dart # Error mapping ``` ### Shared Widget Structure **Shared contains reusable UI components:** ``` shared/ ├── widgets/ │ ├── app_banner.dart # App-wide banner │ ├── app_loading_indicator.dart # Loading indicator │ ├── bottom_nav_bar.dart # Navigation bar │ └── divination/ # Divination domain widgets │ ├── gua_icon.dart # Gua icon widget │ ├── yao_glyph.dart # Yao glyph widget │ └── ... └── theme/ ├── app_theme.dart # Theme definition └── design_tokens.dart # Spacing, radius, colors ``` --- ## Placement Rules ### Where to Put Code | Code Type | Location | Reason | |-----------|----------|--------| | Feature business logic | `features//` | Bounded context | | Cross-feature protocol | `core/` | Shared by multiple features | | Reusable UI widget | `shared/widgets/` | Reusable by multiple screens | | Infrastructure abstraction | `data/` | Cache/network/storage | | Feature repository/model | `features//data/` | Feature-scoped data | ### Decision Tree ``` Is it feature-specific business logic? → Yes: features// → No: Is it reusable UI? → Yes: shared/widgets/ → No: Is it infrastructure? → Yes: data/ → No: Is it cross-feature protocol? → Yes: core/ → No: Re-evaluate ``` ### Forbidden Patterns **❌ Do NOT:** 1. Place feature business repositories in `data/` - Wrong: `data/repositories/auth_repository.dart` - Right: `features/auth/data/repositories/auth_repository.dart` 2. Create directories under `lib/` other than allowed second-level - Wrong: `lib/utils/`, `lib/helpers/`, `lib/constants/` - Right: Use `core/`, `shared/`, or feature-specific locations 3. Put feature-specific UI in `shared/widgets/` - Wrong: `shared/widgets/auth_login_form.dart` - Right: `features/auth/presentation/widgets/login_form.dart` 4. Import feature data layer from other features - Wrong: `import 'package:app/features/auth/data/repositories/auth_repository.dart'` in `features/home/` - Right: Access via app-level facade or DI --- ## Naming Conventions ### Files - **snake_case**: `auth_bloc.dart`, `login_screen.dart` - **Feature prefix for shared**: `app_loading_indicator.dart`, `app_banner.dart` ### Classes - **PascalCase**: `AuthBloc`, `AuthState`, `LoginScreen` - **Suffixes**: - `*Bloc` / `*Cubit` - State management - `*Repository` - Data access - `*Api` - HTTP clients - `*Service` - Business services - `*Screen` - Screen widgets - `*Widget` - Reusable widgets ### Directories - **Plural for collections**: `screens/`, `widgets/`, `models/` - **Singular for features**: `auth/`, `home/`, `divination/` --- ## Examples ### Well-organized Feature: `features/divination/` ``` features/divination/ ├── data/ │ ├── apis/ │ │ └── divination_api.dart # HTTP API │ ├── repositories/ │ │ └── divination_repository.dart # Repository │ ├── services/ │ │ └── voice_recorder.dart # Feature service │ └── models/ │ ├── divination_result.dart # Domain model │ ├── divination_params.dart # Request params │ └── follow_up_message.dart # Message model └── presentation/ └── screens/ ├── divination_screen.dart # Main screen ├── auto_divination_screen.dart # Auto mode ├── manual_divination_screen.dart # Manual mode └── follow_up_chat_screen.dart # Follow-up chat ``` ### Shared Widget: `shared/widgets/app_loading_indicator.dart` ```dart import 'package:flutter/material.dart'; class AppLoadingIndicator extends StatelessWidget { const AppLoadingIndicator({super.key}); @override Widget build(BuildContext context) { return const CircularProgressIndicator(); } } ``` ### Core Infrastructure: `core/logging/logger.dart` ```dart import 'log_service.dart'; class Logger { final String module; Logger(this.module, this._service); static void setLogService(LogService service) { _globalLogService = service; } void error({ required String message, required Object error, required StackTrace stackTrace, }) { _service!.error( message: message, error: error, stackTrace: stackTrace, module: module, ); } } Logger getLogger(String module) => Logger.get(module); ``` --- ## Key Principles ### Directory Contract (Must) 1. **Only allowed second-level directories**: `app/`, `core/`, `data/`, `features/`, `shared/`, `l10n/` 2. **Only one root entry**: `lib/main.dart` 3. **No ad-hoc directories**: No `utils/`, `helpers/`, `constants/` under `lib/` 4. **Feature isolation**: Features should not import each other's data layer ### Layer Boundaries 1. **Presentation** → **Data** (via Repository interface) 2. **Data** → **Core/Infrastructure** (via DI) 3. **Core** → **Nothing** (foundation layer) 4. **Shared** → **Core** (for utilities) 5. **Feature** → **Core** + **Shared** (for cross-cutting concerns) ### Data Layer Boundary (Must) - `data/` = infrastructure abstractions (cache/network/storage) - `features//data/` = feature business repositories/models - **NEVER** mix these boundaries