architecture-feature-first
$
npx mdskill add evanca/flutter-ai-rules/architecture-feature-firstOrganize Flutter apps with layered, feature-first architecture.
- Creates folder structures for UI, logic, and data layers.
- Integrates with dependency injection frameworks and state management.
- Decides layer ownership based on business logic complexity.
- Delivers clean, maintainable codebases for new features.
SKILL.md
.github/skills/architecture-feature-firstView on GitHub ↗
--- name: architecture-feature-first description: Structures Flutter apps using layered architecture (UI / Logic / Data) with feature-first file organization. Use when creating new features, designing the project structure, adding repositories/services/view models (or cubits/providers/notifiers), or wiring dependency injection. State management agnostic. --- # Flutter Architecture — Feature-First Skill This skill defines how to design, structure, and implement Flutter applications using the recommended **layered architecture** with **feature-first** file organization. It is **state management agnostic**: the business logic holder in the UI layer may be named ViewModel, Controller, Cubit, Bloc, Provider, or Notifier — depending on the chosen state management approach. The architectural rules apply equally to all of them. ## When to Use Use this skill when: * Designing the folder/file structure of a new Flutter app or feature. * Creating a new View, ViewModel, Repository, or Service. * Deciding which layer owns a piece of logic. * Wiring dependency injection between components. * Adding a domain (logic) layer for complex business logic. --- ## 1. Layers Separate every app into a **UI Layer** and a **Data Layer**. Add a **Logic (Domain) Layer** between them only for complex apps. ``` ┌──────────────────────────────────────────────────────────────┐ │ UI Layer │ Views + business logic holders │ │ │ (ViewModel / Cubit / Controller / Provider) │ ├──────────────────────────────────────────────────────────────┤ │ Logic Layer │ Use Cases / Interactors (optional) │ ├──────────────────────────────────────────────────────────────┤ │ Data Layer │ Repositories + Services │ └──────────────────────────────────────────────────────────────┘ ``` **Rules:** - Only adjacent layers may communicate. The UI layer must never access a Service directly. - The Logic layer is added **only** when business logic is too complex for the business logic holder or is reused across multiple screens. - Data changes always happen in the Data layer (SSOT = Repository). No mutation in UI or Logic layers. - Follow unidirectional data flow: state flows **down** (Data → UI), events flow **up** (UI → Data). --- ## 2. Feature-First File Structure Organize code by **feature**, not by type. Group all layers belonging to one feature together in a single directory. Each feature directory contains the files needed for that feature, named according to the chosen state management approach: | Approach | Business logic holder file | |---|---| | MVVM / ChangeNotifier | `*_viewmodel.dart` / `*_controller.dart` | | BLoC | `*_cubit.dart` / `*_bloc.dart` | | Provider / Riverpod | `*_provider.dart` / `*_notifier.dart` | Example: an `auth` feature would have files like `auth_viewmodel.dart` (or `auth_cubit.dart`, `auth_provider.dart` depending on approach), `login_screen.dart`, and optionally `login_usecase.dart`. --- ## 3. Component Responsibilities ### View - Describes how to present data to the user; keep logic minimal and only UI-related. - Passes events to the business logic holder in response to user interactions. - Keep views focused on presentation; extract reusable widgets into separate components. - Use `StatelessWidget` when possible; keep build methods simple. ### Business Logic Holder (ViewModel / Cubit / Controller / Provider) - Contains logic to convert app data into UI state and maintains current state needed by the view. - Exposes callbacks (commands) to the View and retrieves/transforms data from repositories. - Business logic must not live in UI widgets; only interact with repositories via the business logic holder. ### Repository - Single Source of Truth (SSOT) for a given type of model data. - The only class allowed to mutate its data; all other classes read from it. - Handles business logic such as caching, error handling, and refreshing data. - Transforms raw data from services into domain models consumed by business logic holders. ### Service - Wraps API endpoints and exposes asynchronous response objects. - Isolates data-loading and holds no state. --- ## 4. Domain Layer (Use Cases) Introduce use cases/interactors **only** when: - Logic is complex or does not fit cleanly in the UI or Data layers. - Logic is reused across multiple business logic holders or merges data from multiple repositories. Do not add a domain layer for simple CRUD apps. --- ## 5. Dependency Injection Use dependency injection to provide components with their dependencies, enabling testability and flexibility. - Supply repositories to business logic holders via constructors. - Supply services to repositories via constructors. - Design all components with well-defined interfaces so implementations can be swapped without changing consumers. --- ## References - [Flutter app architecture guide](https://docs.flutter.dev/app-architecture/guide) - [Architecture case study (Compass app)](https://docs.flutter.dev/app-architecture/case-study) - [Architecture recommendations](https://docs.flutter.dev/app-architecture/recommendations)
More from evanca/flutter-ai-rules
- blocImplements Flutter state management using the bloc library (Bloc and Cubit). Use when creating new features, screens, or state management logic with bloc/cubit, modeling state, wiring Flutter widgets to blocs, or writing bloc/cubit unit tests.
- dart-3-updatesApplies Dart 3 language features in Flutter/Dart code. Use when writing if-else or switch statements, creating new classes, or deciding between a data class and a record.
- effective-dartApplies Effective Dart guidelines in Flutter/Dart code. Use when writing or reviewing Dart code for naming conventions, types, style, imports, file structure, usage patterns, documentation, testing, widgets, state management, or performance.
- firebase-aiIntegrates Firebase AI Logic into Flutter apps. Use when setting up the firebase_ai plugin, calling Gemini models, handling AI service errors, or applying security and privacy considerations for AI features.
- firebase-analyticsIntegrates Firebase Analytics into Flutter apps. Use when setting up analytics, logging events, setting user properties, or configuring event parameters.
- firebase-app-checkIntegrates Firebase App Check into Flutter apps. Use when setting up App Check, selecting providers per platform, using debug providers during development, enabling enforcement, or applying App Check security best practices.
- firebase-authIntegrates Firebase Authentication into Flutter apps. Use when setting up auth, managing auth state, implementing email/password or social sign-in, handling auth errors, managing users, or applying security best practices.
- firebase-cloud-firestoreIntegrates Cloud Firestore into Flutter apps. Use when setting up Firestore, designing document/collection structure, reading and writing data, working with real-time listeners, designing for scale, or applying security rules.
- firebase-cloud-functionsCalls Firebase Cloud Functions from Flutter apps. Use when setting up callable functions, passing data to functions, handling errors from function calls, optimizing performance, or testing with the Firebase Emulator Suite.
- firebase-crashlyticsIntegrates Firebase Crashlytics into Flutter apps. Use when setting up crash reporting, handling fatal and non-fatal errors, customizing crash reports with keys/logs/user identifiers, or configuring opt-in reporting.