# Architecture Layers Detailed guide to layer responsibilities and interactions in Flutter apps. ## Layer Overview ``` ┌─────────────────────────────────────┐ │ UI Layer │ │ ┌────────────┐ ┌─────────────┐ │ │ │ Views │ │ ViewModels │ │ │ └────────────┘ └─────────────┘ │ └─────────────────────────────────────┘ ↓ ┌─────────────────────────────────────┐ │ Domain Layer (Optional) │ │ ┌─────────────────────┐ │ │ │ Use-cases │ │ │ └─────────────────────┘ │ └─────────────────────────────────────┘ ↓ ┌─────────────────────────────────────┐ │ Data Layer │ │ ┌────────────┐ ┌─────────────┐ │ │ │Repositories │ │ Services │ │ │ └────────────┘ └─────────────┘ │ └─────────────────────────────────────┘ ``` ## UI Layer Responsibility: Interact with user, display data, receive input. ### Views **What they do:** - Compose widgets to present data - Handle user interactions (taps, form inputs) - Pass events to ViewModel - Re-render when ViewModel state changes **What they don't do:** - Business logic - Data transformation - State persistence - API calls **Logic allowed in Views:** - Simple if-statements for conditional rendering - Animation logic - Layout logic (responsive design, orientation) - Simple routing ### ViewModels **What they do:** - Transform repository data into UI state - Maintain UI state (survives configuration changes) - Expose commands for user actions - Aggregate data from multiple repositories **What they don't do:** - Direct UI manipulation - Platform API calls - File I/O - Network requests directly (use repositories) **State management:** - Use ChangeNotifier for simple apps - Consider Provider, Riverpod, or Bloc for complex apps - Expose state as Streams or ChangeNotifiers - Call Repositories directly for simple flows; introduce Use-cases only when business logic is complex, reused, or spans multiple Repositories. ## Domain Layer (Optional) **Purpose**: Abstract complex business logic from ViewModels. ### When to add Domain Layer Add when ViewModels have logic that: 1. Merges data from multiple repositories 2. Is exceedingly complex 3. Will be reused by different ViewModels ### Use-cases **What they do:** - Take data from repositories - Transform for UI layer consumption - Encapsulate reusable business logic **Relationships:** - Use-cases depend on Repositories - Use-cases and Repositories: many-to-many - ViewModels may depend on Use-cases for complex operations and on Repositories directly for simple operations. **Pros:** - Avoid code duplication in ViewModels - Improve testability by separating complex logic - Improve code readability in ViewModels **Cons:** - Increases complexity - Additional mocks for testing - Adds boilerplate code **Recommendation**: Add use-cases only when needed. Don't force all data access through use-cases for simple operations. ## Data Layer Responsibility: Handle business data and logic. ### Repositories **What they do:** - Single source of truth for data types - Poll data from Services - Transform raw data into domain models - Handle business logic: - Caching - Error handling - Retry logic - Data refresh (polling, user-triggered) **What they don't do:** - Direct UI rendering - Business logic better suited for Domain layer - Direct knowledge of other repositories **Output:** - Domain models (data classes tailored for app needs) - Exposed as Streams (real-time) or Futures (one-time) **Relationships:** - Many-to-many with ViewModels - Many-to-many with Services - Should not be aware of each other ### Services **What they do:** - Wrap external data sources - Expose async response objects (Future, Stream) - Isolate data loading - One service per data source **What they don't do:** - Hold state (stateless) - Business logic - Data transformation - Caching **Examples:** - Platform APIs (iOS/Android native) - REST/GraphQL API clients - Local file access - Database access - Platform plugins (geolocation, camera, etc.) **Relationships:** - Many-to-many with Repositories - Can be shared across the app ## Layer Communication Rules 1. **Lower layers stay independent**: Data doesn't depend on Domain or UI; Domain doesn't depend on UI. 2. **Unidirectional data flow**: Data -> ViewModel -> View. 3. **Events flow opposite**: View -> ViewModel -> Repository -> Service, or View -> ViewModel -> Use-case -> Repository -> Service when a Use-case is justified. 4. **No direct service calls from UI**: Views and ViewModels don't call Services directly. 5. **No forced Domain layer**: Skipping Use-cases is valid for simple logic. ## When to Use Which Layer **Small CRUD apps:** - UI Layer (Views + ViewModels) - Data Layer (Repositories + Services) - Skip Domain Layer **Apps with complex business logic:** - UI Layer - Domain Layer (for complex, reusable logic) - Data Layer - ViewModels may use Repositories directly for simple operations, Use-cases for complex **Enterprise apps with complex rules:** - All three layers - Consider enforcing Use-cases for all data access ## Testing Strategy **UI Layer Tests:** - Widget tests for Views - Unit tests for ViewModels (mock Repositories) **Domain Layer Tests:** - Unit tests for Use-cases (mock Repositories) **Data Layer Tests:** - Unit tests for Repositories (mock Services) - Integration tests for Services (actual APIs/databases)